#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
+ tbb::atomic<int> ref_count_;\r
+ 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
+ ref_count_ = 0;\r
+ }\r
\r
- STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;}\r
- STDMETHOD_(ULONG, AddRef()) {return 1;}\r
- STDMETHOD_(ULONG, Release()) {return 1;}\r
+ STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;}\r
+ STDMETHOD_(ULONG, AddRef()) \r
+ {\r
+ return ++ref_count_;\r
+ }\r
+ STDMETHOD_(ULONG, Release()) \r
+ {\r
+ --ref_count_;\r
+ if(ref_count_ == 0)\r
+ delete this;\r
+ return ref_count_;\r
+ }\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_(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
+ frame_.reset();\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
size_t preroll_count_;\r
\r
- std::list<std::shared_ptr<IDeckLinkVideoFrame>> frame_container_; // Must be std::list in order to guarantee that pointers are always valid.\r
boost::circular_buffer<std::vector<int16_t>> audio_container_;\r
\r
tbb::concurrent_bounded_queue<std::shared_ptr<core::read_frame>> video_frame_buffer_;\r
else if(result == bmdOutputFrameFlushed)\r
graph_->add_tag("flushed-frame");\r
\r
- frame_container_.erase(std::find_if(frame_container_.begin(), frame_container_.end(), [&](const std::shared_ptr<IDeckLinkVideoFrame>& frame)\r
- {\r
- return frame.get() == completed_frame;\r
- }));\r
-\r
std::shared_ptr<core::read_frame> frame; \r
video_frame_buffer_.pop(frame); \r
schedule_next_video(make_safe(frame)); \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
- if(FAILED(output_->ScheduleVideoFrame(frame_container_.back().get(), (frames_scheduled_++) * format_desc_.duration, format_desc_.duration, format_desc_.time_scale)))\r
+ CComPtr<IDeckLinkVideoFrame> frame2(new decklink_frame(frame, format_desc_, config_.key_only));\r
+ if(FAILED(output_->ScheduleVideoFrame(frame2, (frames_scheduled_++) * format_desc_.duration, format_desc_.duration, format_desc_.time_scale)))\r
CASPAR_LOG(error) << print() << L" Failed to schedule video.";\r
\r
graph_->update_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);\r
const configuration config_;\r
com_context<decklink_consumer> context_;\r
core::video_format_desc format_desc_;\r
- size_t fail_count_;\r
public:\r
\r
decklink_consumer_proxy(const configuration& config)\r
: config_(config)\r
, context_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")\r
- , fail_count_(0)\r
{\r
}\r
\r
\r
virtual bool send(const safe_ptr<core::read_frame>& frame)\r
{\r
- if(!context_)\r
- context_.reset([&]{return new decklink_consumer(config_, format_desc_);});\r
-\r
- try\r
- {\r
- context_->send(frame);\r
- fail_count_ = 0;\r
- }\r
- catch(...)\r
- {\r
- context_.reset();\r
-\r
- if(fail_count_++ > 3)\r
- return false; // Outside didn't handle exception properly, just give up.\r
- \r
- throw;\r
- }\r
-\r
+ context_->send(frame);\r
return true;\r
}\r
\r
{\r
return context_ ? context_->print() : L"decklink_consumer";\r
}\r
-\r
- virtual bool key_only() const\r
- {\r
- return config_.key_only;\r
- }\r
- \r
+ \r
virtual const core::video_format_desc& get_video_format_desc() const\r
{\r
return format_desc_;\r