#include <boost/circular_buffer.hpp>\r
#include <boost/timer.hpp>\r
\r
-namespace caspar { \r
+namespace caspar { namespace decklink { \r
\r
struct configuration\r
{\r
\r
class decklink_frame : public IDeckLinkVideoFrame\r
{\r
+ tbb::atomic<int> ref_count_;\r
std::shared_ptr<core::read_frame> frame_;\r
const core::video_format_desc format_desc_;\r
\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
- , key_only_(key_only){}\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_(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
\r
STDMETHOD(GetBytes(void** buffer))\r
{\r
- static std::vector<uint8_t> zeros(1920*1080*4, 0);\r
+ static std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> zeros(1920*1080*4, 0);\r
if(static_cast<size_t>(frame_->image_data().size()) != format_desc_.size)\r
{\r
*buffer = zeros.data();\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
+ boost::circular_buffer<std::vector<int32_t>> audio_container_;\r
\r
tbb::concurrent_bounded_queue<std::shared_ptr<core::read_frame>> video_frame_buffer_;\r
tbb::concurrent_bounded_queue<std::shared_ptr<core::read_frame>> audio_frame_buffer_;\r
\r
- std::shared_ptr<diagnostics::graph> graph_;\r
+ safe_ptr<diagnostics::graph> graph_;\r
boost::timer tick_timer_;\r
\r
public:\r
video_frame_buffer_.set_capacity(1);\r
audio_frame_buffer_.set_capacity(1);\r
\r
- graph_ = diagnostics::create_graph(narrow(print()));\r
graph_->add_guide("tick-time", 0.5);\r
graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f)); \r
graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));\r
graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));\r
graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));\r
+ graph_->set_text(print());\r
+ diagnostics::register_graph(graph_);\r
\r
enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));\r
\r
\r
void enable_audio()\r
{\r
- if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2, bmdAudioOutputStreamTimestamped)))\r
+ if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable audio output."));\r
\r
if(FAILED(output_->SetAudioCallback(this)))\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
+ schedule_next_video(make_safe_ptr(frame)); \r
}\r
catch(...)\r
{\r
{\r
std::shared_ptr<core::read_frame> frame;\r
audio_frame_buffer_.pop(frame);\r
- schedule_next_audio(make_safe(frame)); \r
+ schedule_next_audio(make_safe_ptr(frame)); \r
}\r
}\r
catch(...)\r
{\r
const int sample_frame_count = frame->audio_data().size()/format_desc_.audio_channels;\r
\r
- audio_container_.push_back(std::vector<int16_t>(frame->audio_data().begin(), frame->audio_data().end()));\r
+ audio_container_.push_back(std::vector<int32_t>(frame->audio_data().begin(), frame->audio_data().end()));\r
\r
if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, (audio_scheduled_++) * sample_frame_count, format_desc_.audio_sample_rate, nullptr)))\r
CASPAR_LOG(error) << print() << L" Failed to schedule audio.";\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_, 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
+ 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
}; \r
\r
-safe_ptr<core::frame_consumer> create_decklink_consumer(const std::vector<std::wstring>& params) \r
+safe_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>& params) \r
{\r
if(params.size() < 1 || params[0] != L"DECKLINK")\r
return core::frame_consumer::empty();\r
return make_safe<decklink_consumer_proxy>(config);\r
}\r
\r
-safe_ptr<core::frame_consumer> create_decklink_consumer(const boost::property_tree::ptree& ptree) \r
+safe_ptr<core::frame_consumer> create_consumer(const boost::property_tree::ptree& ptree) \r
{\r
configuration config;\r
\r
return make_safe<decklink_consumer_proxy>(config);\r
}\r
\r
-}\r
+}}\r
\r
/*\r
##############################################################################\r