\r
namespace internal {\r
\r
-static void* fast_memsfhl(void* dest, const void* source, size_t count, int m1, int m2, int m3, int m4)\r
+static void* fast_memshfl(void* dest, const void* source, size_t count, int m1, int m2, int m3, int m4)\r
{\r
__m128i* dest128 = reinterpret_cast<__m128i*>(dest); \r
const __m128i* source128 = reinterpret_cast<const __m128i*>(source);\r
\r
}\r
\r
-static void* fast_memsfhl(void* dest, const void* source, size_t count, int m1, int m2, int m3, int m4)\r
+static void* fast_memshfl(void* dest, const void* source, size_t count, int m1, int m2, int m3, int m4)\r
{ \r
tbb::affinity_partitioner ap;\r
tbb::parallel_for(tbb::blocked_range<size_t>(0, count/128), [&](const tbb::blocked_range<size_t>& r)\r
{ \r
- internal::fast_memsfhl(reinterpret_cast<char*>(dest) + r.begin()*128, reinterpret_cast<const char*>(source) + r.begin()*128, r.size()*128, m1, m2, m3, m4); \r
+ internal::fast_memshfl(reinterpret_cast<char*>(dest) + r.begin()*128, reinterpret_cast<const char*>(source) + r.begin()*128, r.size()*128, m1, m2, m3, m4); \r
}, ap);\r
\r
return dest;\r
virtual ~frame_consumer() {}\r
\r
virtual bool send(const safe_ptr<read_frame>& frame) = 0;\r
- virtual bool key_only() const{ return false;}\r
virtual void initialize(const video_format_desc& format_desc) = 0;\r
virtual std::wstring print() const = 0;\r
virtual bool has_synchronization_clock() const {return true;}\r
\r
namespace caspar { namespace core {\r
\r
-class deferred_key_read_Frame : public core::read_frame\r
-{\r
- ogl_device& ogl_;\r
- safe_ptr<read_frame> fill_;\r
- std::shared_ptr<host_buffer> key_;\r
- tbb::mutex mutex_;\r
-public:\r
- deferred_key_read_Frame(ogl_device& ogl, const safe_ptr<read_frame>& fill)\r
- : ogl_(ogl)\r
- , fill_(fill)\r
- {\r
- }\r
-\r
- virtual const boost::iterator_range<const uint8_t*> image_data()\r
- {\r
- tbb::mutex::scoped_lock lock(mutex_);\r
- if(!key_)\r
- {\r
- key_ = ogl_.create_host_buffer(fill_->image_data().size(), host_buffer::write_only); \r
- fast_memsfhl(key_->data(), fill_->image_data().begin(), fill_->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
- }\r
-\r
- auto ptr = static_cast<const uint8_t*>(key_->data());\r
- return boost::iterator_range<const uint8_t*>(ptr, ptr + key_->size());\r
- }\r
-\r
- virtual const boost::iterator_range<const int16_t*> audio_data()\r
- {\r
- return fill_->audio_data();\r
- } \r
-};\r
- \r
struct output::implementation\r
{ \r
typedef std::pair<safe_ptr<read_frame>, safe_ptr<read_frame>> fill_and_key;\r
timer_.tick(1.0/channel_.get_format_desc().fps);\r
return;\r
}\r
-\r
- auto fill = frame;\r
- auto key = make_safe<deferred_key_read_Frame>(channel_.ogl(), frame);\r
-\r
+ \r
auto it = consumers_.begin();\r
while(it != consumers_.end())\r
{\r
if(consumer->get_video_format_desc() != channel_.get_format_desc())\r
consumer->initialize(channel_.get_format_desc());\r
\r
- if(consumer->send(consumer->key_only() ? key : fill))\r
+ if(consumer->send(frame))\r
++it;\r
else\r
consumers_.erase(it++);\r
\r
#include <common/concurrency/executor.h>\r
#include <common/diagnostics/graph.h>\r
-#include <common/memory/memcpy.h>\r
#include <common/memory/memclr.h>\r
+#include <common/memory/memcpy.h>\r
+#include <common/memory/memshfl.h>\r
#include <common/utility/timer.h>\r
\r
#include <core/consumer/frame_consumer.h>\r
int preroll_count_;\r
\r
const bool embedded_audio_;\r
+ const bool key_only_;\r
\r
executor executor_;\r
public:\r
- bluefish_consumer(const core::video_format_desc& format_desc, unsigned int device_index, bool embedded_audio) \r
+ bluefish_consumer(const core::video_format_desc& format_desc, unsigned int device_index, bool embedded_audio, bool key_only) \r
: blue_(create_blue(device_index))\r
, device_index_(device_index)\r
, format_desc_(format_desc) \r
, vid_fmt_(get_video_mode(*blue_, format_desc))\r
, preroll_count_(0)\r
, embedded_audio_(embedded_audio)\r
+ , key_only_(key_only)\r
, executor_(print())\r
{\r
executor_.set_capacity(core::consumer_buffer_depth());\r
// Copy to local buffers\r
\r
if(!frame->image_data().empty())\r
- fast_memcpy(reserved_frames_.front()->image_data(), frame->image_data().begin(), frame->image_data().size());\r
+ {\r
+ if(key_only_) \r
+ fast_memshfl(reserved_frames_.front()->image_data(), frame->image_data().begin(), frame->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
+ else\r
+ fast_memcpy(reserved_frames_.front()->image_data(), frame->image_data().begin(), frame->image_data().size());\r
+ }\r
else\r
fast_memclr(reserved_frames_.front()->image_data(), reserved_frames_.front()->image_size());\r
-\r
+ \r
// Sync\r
\r
sync_timer_.restart();\r
virtual void initialize(const core::video_format_desc& format_desc)\r
{\r
format_desc_ = format_desc;\r
- consumer_.reset(new bluefish_consumer(format_desc, device_index_, embedded_audio_));\r
+ consumer_.reset(new bluefish_consumer(format_desc, device_index_, embedded_audio_, key_only_));\r
}\r
\r
virtual bool send(const safe_ptr<core::read_frame>& frame)\r
{\r
if(!consumer_)\r
- consumer_.reset(new bluefish_consumer(format_desc_, device_index_, embedded_audio_));\r
+ consumer_.reset(new bluefish_consumer(format_desc_, device_index_, embedded_audio_, key_only_));\r
\r
try\r
{\r
\r
return L"bluefish [" + boost::lexical_cast<std::wstring>(device_index_) + L"]";\r
}\r
-\r
- virtual bool key_only() const\r
- {\r
- return key_only_;\r
- }\r
}; \r
\r
safe_ptr<core::frame_consumer> create_bluefish_consumer(const std::vector<std::wstring>& params)\r
#include <core/consumer/frame_consumer.h>\r
\r
#include <tbb/concurrent_queue.h>\r
+#include <tbb/cache_aligned_allocator.h>\r
\r
#include <boost/circular_buffer.hpp>\r
#include <boost/timer.hpp>\r
\r
class decklink_frame : public IDeckLinkVideoFrame\r
{\r
- const safe_ptr<core::read_frame> frame_;\r
- const core::video_format_desc format_desc_;\r
+ const std::shared_ptr<core::read_frame> frame_;\r
+ const core::video_format_desc format_desc_;\r
+\r
+ bool key_only_;\r
+ std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> key_data_;\r
public:\r
- decklink_frame(const safe_ptr<core::read_frame>& frame, const core::video_format_desc& format_desc)\r
+ decklink_frame(const safe_ptr<core::read_frame>& frame, const core::video_format_desc& format_desc, bool key_only)\r
: frame_(frame)\r
- , format_desc_(format_desc){}\r
+ , format_desc_(format_desc)\r
+ , key_only_(key_only){}\r
\r
- STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;}\r
- STDMETHOD_(ULONG, AddRef()) {return 1;}\r
- STDMETHOD_(ULONG, Release()) {return 1;}\r
-\r
- STDMETHOD_(long, GetWidth()) {return format_desc_.width;} \r
- STDMETHOD_(long, GetHeight()) {return format_desc_.height;} \r
- STDMETHOD_(long, GetRowBytes()) {return format_desc_.width*4;} \r
- STDMETHOD_(BMDPixelFormat, GetPixelFormat()){return bmdFormat8BitBGRA;} \r
- STDMETHOD_(BMDFrameFlags, GetFlags()) {return bmdFrameFlagDefault;}\r
+ STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;}\r
+ STDMETHOD_(ULONG, AddRef()) {return 1;}\r
+ STDMETHOD_(ULONG, Release()) {return 1;}\r
+\r
+ STDMETHOD_(long, GetWidth()) {return format_desc_.width;} \r
+ STDMETHOD_(long, GetHeight()) {return format_desc_.height;} \r
+ STDMETHOD_(long, GetRowBytes()) {return format_desc_.width*4;} \r
+ STDMETHOD_(BMDPixelFormat, GetPixelFormat()) {return bmdFormat8BitBGRA;} \r
+ STDMETHOD_(BMDFrameFlags, GetFlags()) {return bmdFrameFlagDefault;}\r
\r
STDMETHOD(GetBytes(void** buffer))\r
{\r
static std::vector<uint8_t> zeros(1920*1080*4, 0);\r
- *buffer = const_cast<uint8_t*>(frame_->image_data().begin());\r
if(static_cast<size_t>(frame_->image_data().size()) != format_desc_.size)\r
+ {\r
*buffer = zeros.data();\r
+ return S_OK;\r
+ }\r
+\r
+ if(!key_only_)\r
+ *buffer = const_cast<uint8_t*>(frame_->image_data().begin());\r
+ else\r
+ {\r
+ if(key_data_.empty())\r
+ {\r
+ key_data_.resize(frame_->image_data().size());\r
+ fast_memshfl(key_data_.data(), frame_->image_data().begin(), frame_->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
+ }\r
+ *buffer = key_data_.data();\r
+ }\r
+\r
return S_OK;\r
}\r
\r
\r
struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable\r
{ \r
- const configuration config_;\r
+ const configuration config_;\r
\r
CComPtr<IDeckLink> decklink_;\r
CComQIPtr<IDeckLinkOutput> output_;\r
\r
void schedule_next_video(const safe_ptr<core::read_frame>& frame)\r
{\r
- frame_container_.push_back(std::make_shared<decklink_frame>(frame, format_desc_));\r
+ frame_container_.push_back(std::make_shared<decklink_frame>(frame, format_desc_, config_.key_only));\r
if(FAILED(output_->ScheduleVideoFrame(frame_container_.back().get(), (frames_scheduled_++) * format_desc_.duration, format_desc_.duration, format_desc_.time_scale)))\r
CASPAR_LOG(error) << print() << L" Failed to schedule video.";\r
\r
#include <common/log/log.h>\r
#include <common/memory/safe_ptr.h>\r
#include <common/memory/memcpy.h>\r
+#include <common/memory/memshfl.h>\r
#include <common/utility/timer.h>\r
#include <common/utility/string.h>\r
\r
#include <tbb/concurrent_queue.h>\r
\r
#include <algorithm>\r
-#include <array>\r
+#include <vector>\r
\r
namespace caspar {\r
\r
core::video_format_desc format_desc_;\r
\r
GLuint texture_;\r
+ std::vector<GLuint> pbos_;\r
\r
float width_;\r
float height_;\r
\r
boost::thread thread_;\r
tbb::atomic<bool> is_running_;\r
+\r
+ const bool key_only_;\r
public:\r
- ogl_consumer(unsigned int screen_index, stretch stretch, bool windowed, const core::video_format_desc& format_desc) \r
+ ogl_consumer(unsigned int screen_index, stretch stretch, bool windowed, const core::video_format_desc& format_desc, bool key_only) \r
: format_desc_(format_desc)\r
, texture_(0)\r
+ , pbos_(2, 0)\r
, stretch_(stretch)\r
, windowed_(windowed)\r
, screen_index_(screen_index) \r
, square_height_(format_desc.height)\r
, graph_(diagnostics::create_graph(narrow(print())))\r
, input_buffer_(core::consumer_buffer_depth()-1)\r
+ , key_only_(key_only)\r
{ \r
frame_buffer_.set_capacity(2);\r
\r
GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));\r
GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, format_desc_.width, format_desc_.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0));\r
GL(glBindTexture(GL_TEXTURE_2D, 0));\r
+ \r
+ GL(glGenBuffers(2, pbos_.data()));\r
\r
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[0]);\r
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB);\r
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbos_[1]);\r
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, format_desc_.size, 0, GL_STREAM_DRAW_ARB);\r
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);\r
+\r
CASPAR_LOG(info) << print() << " Sucessfully Initialized.";\r
}\r
\r
if(texture_)\r
glDeleteTextures(1, &texture_);\r
\r
+ BOOST_FOREACH(auto& pbo, pbos_)\r
+ {\r
+ if(pbo)\r
+ glDeleteBuffers(1, &pbo);\r
+ }\r
+\r
CASPAR_LOG(info) << print() << " Sucessfully Uninitialized.";\r
}\r
\r
if(frame->image_data().empty())\r
return;\r
\r
- GL(glBindTexture(GL_TEXTURE_2D, texture_));\r
+ glBindTexture(GL_TEXTURE_2D, texture_);\r
+\r
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[0]);\r
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, format_desc_.width, format_desc_.height, GL_BGRA, GL_UNSIGNED_BYTE, 0);\r
\r
- GL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, format_desc_.width, format_desc_.height, GL_BGRA, GL_UNSIGNED_BYTE, frame->image_data().begin()));\r
- \r
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos_[1]);\r
+ glBufferData(GL_PIXEL_UNPACK_BUFFER, format_desc_.size, 0, GL_STREAM_DRAW);\r
+\r
+ auto ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);\r
+ if(ptr)\r
+ {\r
+ if(key_only_)\r
+ fast_memshfl(reinterpret_cast<char*>(ptr), frame->image_data().begin(), frame->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
+ else\r
+ fast_memcpy(reinterpret_cast<char*>(ptr), frame->image_data().begin(), frame->image_data().size());\r
+\r
+ glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer\r
+ }\r
+\r
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);\r
+ \r
GL(glClear(GL_COLOR_BUFFER_BIT)); \r
glBegin(GL_QUADS);\r
glTexCoord2f(0.0f, 1.0f); glVertex2f(-width_, -height_);\r
glEnd();\r
\r
glBindTexture(GL_TEXTURE_2D, 0);\r
+\r
+ std::rotate(pbos_.begin(), pbos_.begin() + 1, pbos_.end());\r
}\r
\r
void send(const safe_ptr<core::read_frame>& frame)\r
\r
virtual void initialize(const core::video_format_desc& format_desc)\r
{\r
- consumer_.reset(new ogl_consumer(screen_index_, stretch_, windowed_, format_desc));\r
+ consumer_.reset(new ogl_consumer(screen_index_, stretch_, windowed_, format_desc, key_only_));\r
}\r
\r
virtual bool send(const safe_ptr<core::read_frame>& frame)\r
return consumer_->print();\r
}\r
\r
- virtual bool key_only() const\r
- {\r
- return key_only_;\r
- }\r
-\r
virtual bool has_synchronization_clock() const \r
{\r
return false;\r
</producers>\r
<channels>\r
<channel>\r
- <video-mode>PAL</video-mode>\r
+ <video-mode>1080p5000</video-mode>\r
<consumers>\r
<decklink>\r
<device>1</device>\r
<embedded-audio>true</embedded-audio>\r
<external-key>true</external-key>\r
+ <key-only>true</key-only>\r
</decklink>\r
<screen>\r
- <key-only>true</key-only>\r
<device>1</device>\r
+ <key-only>true</key-only>\r
</screen>\r
</consumers>\r
</channel>\r
+ <channel>\r
+ <video-mode>1080p5000</video-mode>\r
+ <consumers>\r
+ <decklink>\r
+ <device>2</device>\r
+ <embedded-audio>true</embedded-audio>\r
+ <external-key>true</external-key>\r
+ </decklink>\r
+ </consumers>\r
+ </channel>\r
</channels>\r
<controllers>\r
<tcp>\r