, latency(default_latency){}\r
};\r
\r
+class decklink_frame_adapter : public IDeckLinkVideoFrame\r
+{\r
+ safe_ptr<const core::read_frame> frame_;\r
+ core::video_format_desc format_desc_;\r
+public:\r
+ decklink_frame_adapter(const safe_ptr<const core::read_frame>& frame, const core::video_format_desc& format_desc)\r
+ : frame_(frame)\r
+ , format_desc_(format_desc){}\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
+ \r
+ STDMETHOD(GetBytes(void** buffer))\r
+ {\r
+ static std::vector<unsigned char> zeros(1920*1080*4, 0);\r
+ *buffer = const_cast<unsigned char*>(frame_->image_data().begin());\r
+ if(static_cast<size_t>(frame_->image_data().size()) != format_desc_.size)\r
+ *buffer = zeros.data();\r
+ return S_OK;\r
+ }\r
+ \r
+ STDMETHOD(GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode)){return S_FALSE;} \r
+ STDMETHOD(GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary)) {return S_FALSE;}\r
+};\r
+\r
struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable\r
{ \r
const configuration config_;\r
\r
const std::wstring model_name_;\r
const core::video_format_desc format_desc_;\r
+ const size_t buffer_size_;\r
\r
unsigned long frames_scheduled_;\r
unsigned long audio_scheduled_;\r
\r
- std::vector<std::pair<void*, CComPtr<IDeckLinkMutableVideoFrame>>> reserved_frames_;\r
+ std::list<decklink_frame_adapter> frame_container_; // Must be std::list in order to guarantee that pointers are always valid.\r
boost::circular_buffer<std::vector<short>> audio_container_;\r
\r
tbb::concurrent_bounded_queue<std::shared_ptr<const core::read_frame>> video_frame_buffer_;\r
std::shared_ptr<diagnostics::graph> graph_;\r
boost::timer tick_timer_;\r
\r
- size_t buffer_size_;\r
\r
public:\r
decklink_consumer(const configuration& config, const core::video_format_desc& format_desc) \r
, keyer_(decklink_)\r
, model_name_(get_model_name(decklink_))\r
, format_desc_(format_desc)\r
+ , buffer_size_(config.embedded_audio ? 5 : 4) // Minimum buffer-size (3 + 1 tolerance).\r
, frames_scheduled_(0)\r
, audio_scheduled_(0)\r
- , buffer_size_(4) // Minimum buffer-size (3 + 1 tolerance).\r
+ , audio_container_(buffer_size_+1)\r
{\r
is_running_ = true;\r
- \r
+ \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.1f, 0.7f, 0.8f));\r
\r
set_latency(config.latency); \r
set_keyer(config.keyer);\r
- \r
- audio_container_.set_capacity(buffer_size_+1);\r
- allocate_frames(buffer_size_+1);\r
- \r
- CASPAR_LOG(info) << print() << L" Buffer-depth: " << buffer_size_;\r
- \r
+ \r
for(size_t n = 0; n < buffer_size_; ++n)\r
schedule_next_video(core::read_frame::empty());\r
-\r
- video_frame_buffer_.set_capacity(2);\r
- audio_frame_buffer_.set_capacity(2);\r
\r
if(config.embedded_audio)\r
output_->BeginAudioPreroll();\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback."));\r
}\r
\r
+ CASPAR_LOG(info) << print() << L" Buffer depth: " << buffer_size_; \r
CASPAR_LOG(info) << print() << L" Successfully initialized for " << format_desc_.name; \r
}\r
\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not set audio callback."));\r
\r
CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";\r
-\r
- buffer_size_ = 5; // Minimum buffer-size with embedded-audio (4 + 1 tolerance).\r
}\r
\r
void enable_video(BMDDisplayMode display_mode)\r
if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to set playback completion callback."));\r
}\r
-\r
- void allocate_frames(size_t count)\r
- {\r
- std::pair<void*, CComPtr<IDeckLinkMutableVideoFrame>> frame;\r
- std::generate_n(std::back_inserter(reserved_frames_), count, [&]() -> decltype(frame)\r
- {\r
- if(FAILED(output_->CreateVideoFrame(format_desc_.width, format_desc_.height, format_desc_.size/format_desc_.height, bmdFormat8BitBGRA, bmdFrameFlagDefault, &frame.second)))\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to create frame."));\r
-\r
- if(FAILED(frame.second->GetBytes(&frame.first)) || frame.first == nullptr)\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to get frame bytes."));\r
-\r
- return frame;\r
- });\r
- }\r
-\r
+ \r
STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;}\r
STDMETHOD_(ULONG, AddRef()) {return 1;}\r
STDMETHOD_(ULONG, Release()) {return 1;}\r
\r
- STDMETHOD(ScheduledFrameCompleted(IDeckLinkVideoFrame* /*completedFrame*/, BMDOutputFrameCompletionResult result))\r
+ STDMETHOD(ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result))\r
{\r
if(!is_running_)\r
return E_FAIL;\r
+ \r
+ try\r
+ {\r
+ if(result == bmdOutputFrameDisplayedLate)\r
+ graph_->add_tag("late-frame");\r
+ else if(result == bmdOutputFrameDropped)\r
+ graph_->add_tag("dropped-frame");\r
+ else if(result == bmdOutputFrameFlushed)\r
+ graph_->add_tag("flushed-frame");\r
+\r
+ frame_container_.erase(std::remove_if(frame_container_.begin(), frame_container_.end(), [&](const decklink_frame_adapter& frame)\r
+ {\r
+ return &frame == completed_frame;\r
+ }), frame_container_.end());\r
\r
- if(result == bmdOutputFrameDisplayedLate)\r
- graph_->add_tag("late-frame");\r
- else if(result == bmdOutputFrameDropped)\r
- graph_->add_tag("dropped-frame");\r
- else if(result == bmdOutputFrameFlushed)\r
- graph_->add_tag("flushed-frame");\r
-\r
- std::shared_ptr<const core::read_frame> frame; \r
- video_frame_buffer_.pop(frame); \r
- schedule_next_video(safe_ptr<const core::read_frame>(frame));\r
+ std::shared_ptr<const core::read_frame> frame; \r
+ video_frame_buffer_.pop(frame); \r
+ schedule_next_video(safe_ptr<const core::read_frame>(frame)); \r
+ }\r
+ catch(...)\r
+ {\r
+ exception_ = std::current_exception();\r
+ return E_FAIL;\r
+ }\r
\r
return S_OK;\r
}\r
\r
void schedule_next_video(const safe_ptr<const core::read_frame>& frame)\r
{\r
- if(static_cast<size_t>(frame->image_data().size()) == format_desc_.size)\r
- fast_memcpy(reserved_frames_.front().first, frame->image_data().begin(), frame->image_data().size());\r
- else\r
- fast_memclr(reserved_frames_.front().first, format_desc_.size);\r
-\r
- if(FAILED(output_->ScheduleVideoFrame(reserved_frames_.front().second, (frames_scheduled_++) * format_desc_.duration, format_desc_.duration, format_desc_.time_scale)))\r
+ frame_container_.push_back(decklink_frame_adapter(frame, format_desc_));\r
+ if(FAILED(output_->ScheduleVideoFrame(&frame_container_.back(), (frames_scheduled_++) * format_desc_.duration, format_desc_.duration, format_desc_.time_scale)))\r
CASPAR_LOG(error) << print() << L" Failed to schedule video.";\r
\r
- std::rotate(reserved_frames_.begin(), reserved_frames_.begin() + 1, reserved_frames_.end());\r
graph_->update_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);\r
tick_timer_.restart();\r
}\r
\r
if(!is_running_)\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Is not running."));\r
-\r
- video_frame_buffer_.push(frame);\r
+ \r
if(config_.embedded_audio)\r
- audio_frame_buffer_.push(frame); \r
+ audio_frame_buffer_.push(frame); \r
+ video_frame_buffer_.push(frame); \r
}\r
\r
std::wstring print() const\r