X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdecklink%2Fconsumer%2Fdecklink_consumer.cpp;h=7699f7fd846894dcb91150690de41776013fca30;hb=ef45ae47b4f50ef057041b5122d0f34c1659cc11;hp=5a56633736832e2834bd69ca58b2948dc9522e25;hpb=e7edb770fae31e75311a10d7f45bd773a4a62104;p=casparcg diff --git a/modules/decklink/consumer/decklink_consumer.cpp b/modules/decklink/consumer/decklink_consumer.cpp index 5a5663373..7699f7fd8 100644 --- a/modules/decklink/consumer/decklink_consumer.cpp +++ b/modules/decklink/consumer/decklink_consumer.cpp @@ -26,130 +26,207 @@ #include "../interop/DeckLinkAPI_h.h" -#include +#include -#include - -#include +#include #include #include -#include +#include +#include +#include + +#include #include +#include #include #include -#include +namespace caspar { + +struct configuration +{ + size_t device_index; + bool embedded_audio; + bool internal_key; + bool low_latency; + bool key_only; + size_t buffer_depth; + + configuration() + : device_index(1) + , embedded_audio(false) + , internal_key(false) + , low_latency(false) + , key_only(false) + , buffer_depth(core::consumer_buffer_depth()){} +}; -#pragma warning(push) -#pragma warning(disable : 4996) +class decklink_frame : public IDeckLinkVideoFrame +{ + const std::shared_ptr frame_; + const core::video_format_desc format_desc_; - #include + bool key_only_; + std::vector> key_data_; +public: + decklink_frame(const safe_ptr& frame, const core::video_format_desc& format_desc, bool key_only) + : frame_(frame) + , format_desc_(format_desc) + , key_only_(key_only){} + + STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;} + STDMETHOD_(ULONG, AddRef()) {return 1;} + STDMETHOD_(ULONG, Release()) {return 1;} + + STDMETHOD_(long, GetWidth()) {return format_desc_.width;} + STDMETHOD_(long, GetHeight()) {return format_desc_.height;} + STDMETHOD_(long, GetRowBytes()) {return format_desc_.width*4;} + STDMETHOD_(BMDPixelFormat, GetPixelFormat()) {return bmdFormat8BitBGRA;} + STDMETHOD_(BMDFrameFlags, GetFlags()) {return bmdFrameFlagDefault;} + + STDMETHOD(GetBytes(void** buffer)) + { + static std::vector zeros(1920*1080*4, 0); + if(static_cast(frame_->image_data().size()) != format_desc_.size) + { + *buffer = zeros.data(); + return S_OK; + } - #include - #include + if(!key_only_) + *buffer = const_cast(frame_->image_data().begin()); + else + { + if(key_data_.empty()) + { + key_data_.resize(frame_->image_data().size()); + fast_memshfl(key_data_.data(), frame_->image_data().begin(), frame_->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303); + } + *buffer = key_data_.data(); + } -#pragma warning(push) + return S_OK; + } + + STDMETHOD(GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode)){return S_FALSE;} + STDMETHOD(GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary)) {return S_FALSE;} +}; -namespace caspar { - -struct decklink_output : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable +struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable { - struct co_init - { - co_init(){CoInitialize(nullptr);} - ~co_init(){CoUninitialize();} - } co_; - - std::wstring model_name_; - const size_t device_index_; - tbb::atomic is_running_; + const configuration config_; - std::shared_ptr graph_; - boost::timer perf_timer_; + CComPtr decklink_; + CComQIPtr output_; + CComQIPtr configuration_; + CComQIPtr keyer_; - std::array>, 3> reserved_frames_; - boost::circular_buffer> audio_container_; - - const bool embed_audio_; - const bool internal_key; + tbb::spin_mutex exception_mutex_; + std::exception_ptr exception_; - CComPtr decklink_; - CComQIPtr output_; - CComQIPtr keyer_; - - core::video_format_desc format_desc_; + tbb::atomic is_running_; + + const std::wstring model_name_; + const core::video_format_desc format_desc_; + const size_t buffer_size_; + + long long frames_scheduled_; + long long audio_scheduled_; - BMDTimeScale frame_time_scale_; - BMDTimeValue frame_duration_; - unsigned long frames_scheduled_; - unsigned long audio_scheduled_; + size_t preroll_count_; + + std::list> frame_container_; // Must be std::list in order to guarantee that pointers are always valid. + boost::circular_buffer> audio_container_; + + tbb::concurrent_bounded_queue> video_frame_buffer_; + tbb::concurrent_bounded_queue> audio_frame_buffer_; - tbb::concurrent_bounded_queue> video_frame_buffer_; - tbb::concurrent_bounded_queue> audio_frame_buffer_; + std::shared_ptr graph_; + boost::timer tick_timer_; public: - decklink_output(const core::video_format_desc& format_desc,size_t device_index, bool embed_audio, bool internalKey) - : model_name_(L"DECKLINK") - , device_index_(device_index) - , audio_container_(5) - , embed_audio_(embed_audio) - , internal_key(internalKey) + decklink_consumer(const configuration& config, const core::video_format_desc& format_desc) + : config_(config) + , decklink_(get_device(config.device_index)) + , output_(decklink_) + , configuration_(decklink_) + , keyer_(decklink_) + , model_name_(get_model_name(decklink_)) + , format_desc_(format_desc) + , buffer_size_(config.embedded_audio ? config.buffer_depth + 1 : config.buffer_depth) // Minimum buffer-size 3. , frames_scheduled_(0) , audio_scheduled_(0) - , format_desc_(format_desc) + , preroll_count_(0) + , audio_container_(buffer_size_+1) { is_running_ = true; - format_desc_ = format_desc; - CComPtr pDecklinkIterator; - if(FAILED(pDecklinkIterator.CoCreateInstance(CLSID_CDeckLinkIterator))) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " No Decklink drivers installed.")); - - size_t n = 0; - while(n < device_index_ && pDecklinkIterator->Next(&decklink_) == S_OK){++n;} - - if(n != device_index_ || !decklink_) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Decklink card not found.") << arg_name_info("device_index") << arg_value_info(boost::lexical_cast(device_index_))); - - output_ = decklink_; - keyer_ = decklink_; - - BSTR pModelName; - decklink_->GetModelName(&pModelName); - model_name_ = std::wstring(pModelName); + video_frame_buffer_.set_capacity(1); + audio_frame_buffer_.set_capacity(1); + graph_ = diagnostics::create_graph(narrow(print())); graph_->add_guide("tick-time", 0.5); - graph_->set_color("tick-time", diagnostics::color(0.1f, 0.7f, 0.8f)); + graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f)); + graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f)); + graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f)); + graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f)); - auto display_mode = get_display_mode(output_.p, format_desc_.format); - if(display_mode == nullptr) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Card does not support requested videoformat.")); - - display_mode->GetFrameRate(&frame_duration_, &frame_time_scale_); + enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault)); + + if(config.embedded_audio) + enable_audio(); - BMDDisplayModeSupport displayModeSupport; - if(FAILED(output_->DoesSupportVideoMode(display_mode->GetDisplayMode(), bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, &displayModeSupport, nullptr))) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Card does not support requested videoformat.")); - - if(embed_audio_) - { - if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2, bmdAudioOutputStreamTimestamped))) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable audio output.")); + set_latency(config.low_latency); + set_keyer(config.internal_key); - if(FAILED(output_->SetAudioCallback(this))) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not set audio callback.")); + if(config.embedded_audio) + output_->BeginAudioPreroll(); + + for(size_t n = 0; n < buffer_size_; ++n) + schedule_next_video(make_safe()); - CASPAR_LOG(info) << print() << L" Enabled embedded-audio."; - } + if(!config.embedded_audio) + start_playback(); + } - if(FAILED(output_->EnableVideoOutput(display_mode->GetDisplayMode(), bmdVideoOutputFlagDefault))) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable video output.")); + ~decklink_consumer() + { + is_running_ = false; + video_frame_buffer_.try_push(std::make_shared()); + audio_frame_buffer_.try_push(std::make_shared()); - if(FAILED(output_->SetScheduledFrameCompletionCallback(this))) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to set playback completion callback.")); + if(output_ != nullptr) + { + output_->StopScheduledPlayback(0, nullptr, 0); + if(config_.embedded_audio) + output_->DisableAudioOutput(); + output_->DisableVideoOutput(); + } + } + const core::video_format_desc& get_video_format_desc() const + { + return format_desc_; + } + + void set_latency(bool low_latency) + { + if(!low_latency) + { + configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false); + CASPAR_LOG(info) << print() << L" Enabled normal-latency mode"; + } + else + { + configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true); + CASPAR_LOG(info) << print() << L" Enabled low-latency mode"; + } + } + + void set_keyer(bool internal_key) + { if(internal_key) { if(FAILED(keyer_->Enable(FALSE))) @@ -157,212 +234,317 @@ public: else if(FAILED(keyer_->SetLevel(255))) CASPAR_LOG(error) << print() << L" Failed to set key-level to max."; else - CASPAR_LOG(info) << print() << L" Successfully configured internal keyer."; + CASPAR_LOG(info) << print() << L" Enabled internal keyer."; } else { if(FAILED(keyer_->Enable(TRUE))) CASPAR_LOG(error) << print() << L" Failed to enable external keyer."; + else if(FAILED(keyer_->SetLevel(255))) + CASPAR_LOG(error) << print() << L" Failed to set key-level to max."; else - CASPAR_LOG(info) << print() << L" Successfully configured external keyer."; + CASPAR_LOG(info) << print() << L" Enabled external keyer."; } - - for(size_t n = 0; n < reserved_frames_.size(); ++n) - { - if(FAILED(output_->CreateVideoFrame(format_desc_.width, format_desc_.height, format_desc_.size/format_desc_.height, bmdFormat8BitBGRA, bmdFrameFlagDefault, &reserved_frames_[n].second))) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to create frame.")); + } + + void enable_audio() + { + if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2, bmdAudioOutputStreamTimestamped))) + BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable audio output.")); + + if(FAILED(output_->SetAudioCallback(this))) + BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not set audio callback.")); - if(FAILED(reserved_frames_[n].second->GetBytes(&reserved_frames_[n].first))) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to get frame bytes.")); - } - - auto buffer_size = static_cast(frame_time_scale_/frame_duration_)/4; - for(size_t n = 0; n < buffer_size; ++n) - schedule_next_video(core::read_frame::empty()); - - video_frame_buffer_.set_capacity(buffer_size); - audio_frame_buffer_.set_capacity(buffer_size); - for(size_t n = 0; n < std::max(2, buffer_size-2); ++n) - { - video_frame_buffer_.try_push(core::read_frame::empty()); - if(embed_audio_) - audio_frame_buffer_.try_push(core::read_frame::empty()); - } - - if(FAILED(output_->StartScheduledPlayback(0, frame_time_scale_, 1.0))) - BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback.")); - - CASPAR_LOG(info) << print() << L" Successfully initialized for " << format_desc_.name; + CASPAR_LOG(info) << print() << L" Enabled embedded-audio."; } - ~decklink_output() - { - is_running_ = false; - video_frame_buffer_.try_push(core::read_frame::empty()); - audio_frame_buffer_.try_push(core::read_frame::empty()); + void enable_video(BMDDisplayMode display_mode) + { + if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault))) + BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable video output.")); + + if(FAILED(output_->SetScheduledFrameCompletionCallback(this))) + BOOST_THROW_EXCEPTION(caspar_exception() + << msg_info(narrow(print()) + " Failed to set playback completion callback.") + << boost::errinfo_api_function("SetScheduledFrameCompletionCallback")); + } - if(output_ != nullptr) - { - output_->StopScheduledPlayback(0, nullptr, 0); - if(embed_audio_) - output_->DisableAudioOutput(); - output_->DisableVideoOutput(); - } - CASPAR_LOG(info) << print() << L" Shutting down."; + void start_playback() + { + if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0))) + BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback.")); } - - virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID*) {return E_NOINTERFACE;} - virtual ULONG STDMETHODCALLTYPE AddRef () {return 1;} - virtual ULONG STDMETHODCALLTYPE Release () {return 1;} - virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted (IDeckLinkVideoFrame* /*completedFrame*/, BMDOutputFrameCompletionResult /*result*/) + STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;} + STDMETHOD_(ULONG, AddRef()) {return 1;} + STDMETHOD_(ULONG, Release()) {return 1;} + + STDMETHOD(ScheduledPlaybackHasStopped()) { - if(!is_running_) - return S_OK; - - std::shared_ptr frame; - video_frame_buffer_.pop(frame); - schedule_next_video(safe_ptr(frame)); - + is_running_ = false; + CASPAR_LOG(info) << print() << L" Scheduled playback has stopped."; return S_OK; } - virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped (void) + STDMETHOD(ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)) { + if(!is_running_) + return E_FAIL; + + try + { + if(result == bmdOutputFrameDisplayedLate) + { + graph_->add_tag("late-frame"); + ++frames_scheduled_; + ++audio_scheduled_; + } + else if(result == bmdOutputFrameDropped) + graph_->add_tag("dropped-frame"); + else if(result == bmdOutputFrameFlushed) + graph_->add_tag("flushed-frame"); + + frame_container_.erase(std::find_if(frame_container_.begin(), frame_container_.end(), [&](const std::shared_ptr& frame) + { + return frame.get() == completed_frame; + })); + + std::shared_ptr frame; + video_frame_buffer_.pop(frame); + schedule_next_video(make_safe(frame)); + } + catch(...) + { + tbb::spin_mutex::scoped_lock lock(exception_mutex_); + exception_ = std::current_exception(); + return E_FAIL; + } + return S_OK; } - virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples (BOOL /*preroll*/) + STDMETHOD(RenderAudioSamples(BOOL preroll)) { if(!is_running_) - return S_OK; - - std::shared_ptr frame; - audio_frame_buffer_.pop(frame); - schedule_next_audio(safe_ptr(frame)); + return E_FAIL; + + try + { + if(preroll) + { + if(++preroll_count_ >= buffer_size_) + { + output_->EndAudioPreroll(); + start_playback(); + } + else + schedule_next_audio(make_safe()); + } + else + { + std::shared_ptr frame; + audio_frame_buffer_.pop(frame); + schedule_next_audio(make_safe(frame)); + } + } + catch(...) + { + tbb::spin_mutex::scoped_lock lock(exception_mutex_); + exception_ = std::current_exception(); + return E_FAIL; + } return S_OK; } - void schedule_next_audio(const safe_ptr& frame) + void schedule_next_audio(const safe_ptr& frame) { - static std::vector silence(48000, 0); - - int audio_samples = static_cast(48000.0 / format_desc_.fps); + const int sample_frame_count = frame->audio_data().size()/format_desc_.audio_channels; - auto frame_audio_data = frame->audio_data().empty() ? silence.data() : const_cast(frame->audio_data().begin()); + audio_container_.push_back(std::vector(frame->audio_data().begin(), frame->audio_data().end())); - audio_container_.push_back(std::vector(frame_audio_data, frame_audio_data+audio_samples*2)); - - if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), audio_samples, (audio_scheduled_++) * audio_samples, 48000, nullptr))) + if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, (audio_scheduled_++) * sample_frame_count, format_desc_.audio_sample_rate, nullptr))) CASPAR_LOG(error) << print() << L" Failed to schedule audio."; } - void schedule_next_video(const safe_ptr& frame) + void schedule_next_video(const safe_ptr& frame) { - if(!frame->image_data().empty()) - std::copy(frame->image_data().begin(), frame->image_data().end(), static_cast(reserved_frames_.front().first)); - else - std::fill_n(static_cast(reserved_frames_.front().first), 0, format_desc_.size/4); - - if(FAILED(output_->ScheduleVideoFrame(reserved_frames_.front().second, (frames_scheduled_++) * frame_duration_, frame_duration_, frame_time_scale_))) + frame_container_.push_back(std::make_shared(frame, format_desc_, config_.key_only)); + if(FAILED(output_->ScheduleVideoFrame(frame_container_.back().get(), (frames_scheduled_++) * format_desc_.duration, format_desc_.duration, format_desc_.time_scale))) CASPAR_LOG(error) << print() << L" Failed to schedule video."; - std::rotate(reserved_frames_.begin(), reserved_frames_.begin() + 1, reserved_frames_.end()); - graph_->update_value("tick-time", static_cast(perf_timer_.elapsed()/format_desc_.interval*0.5)); - perf_timer_.restart(); + graph_->update_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5); + tick_timer_.restart(); } - void send(const safe_ptr& frame) + void send(const safe_ptr& frame) { - video_frame_buffer_.push(frame); - if(embed_audio_) - audio_frame_buffer_.push(frame); - } + { + tbb::spin_mutex::scoped_lock lock(exception_mutex_); + if(exception_ != nullptr) + std::rethrow_exception(exception_); + } + if(!is_running_) + BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Is not running.")); + + if(config_.embedded_audio) + audio_frame_buffer_.push(frame); + video_frame_buffer_.push(frame); + } + std::wstring print() const { - return model_name_ + L" [" + boost::lexical_cast(device_index_) + L"]"; + return model_name_ + L" [" + boost::lexical_cast(config_.device_index) + L"|" + format_desc_.name + L"]"; } }; -struct decklink_consumer::implementation +struct decklink_consumer_proxy : public core::frame_consumer { - std::unique_ptr input_; - size_t device_index_; - bool embed_audio_; - bool internal_key_; - - executor executor_; + const configuration config_; + com_context context_; + core::video_format_desc format_desc_; + size_t fail_count_; public: - implementation(size_t device_index, bool embed_audio, bool internal_key) - : device_index_(device_index) - , embed_audio_(embed_audio) - , internal_key_(internal_key) - , executor_(L"DECKLINK[" + boost::lexical_cast(device_index) + L"]") + decklink_consumer_proxy(const configuration& config) + : config_(config) + , context_(L"decklink_consumer[" + boost::lexical_cast(config.device_index) + L"]") + , fail_count_(0) { - executor_.start(); } - ~implementation() + ~decklink_consumer_proxy() { - executor_.invoke([&] - { - input_ = nullptr; - }); + auto str = print(); + context_.reset(); + CASPAR_LOG(info) << str << L" Successfully Uninitialized."; } - - void initialize(const core::video_format_desc& format_desc) + + virtual void initialize(const core::video_format_desc& format_desc) { - executor_.invoke([&] + format_desc_ = format_desc; + context_.reset([&]{return new decklink_consumer(config_, format_desc_);}); + + CASPAR_LOG(info) << print() << L" Successfully Initialized."; + } + + virtual bool send(const safe_ptr& frame) + { + if(!context_) + context_.reset([&]{return new decklink_consumer(config_, format_desc_);}); + + try + { + context_->send(frame); + fail_count_ = 0; + } + catch(...) { - input_.reset(new decklink_output(format_desc, device_index_, embed_audio_, internal_key_)); - }); + context_.reset(); + + if(fail_count_++ > 3) + return false; // Outside didn't handle exception properly, just give up. + + throw; + } + + return true; } - void send(const safe_ptr& frame) + virtual std::wstring print() const { - input_->send(frame); + return context_ ? context_->print() : L"decklink_consumer"; } - size_t buffer_depth() const + virtual bool key_only() const { - return 1; + return config_.key_only; } - - std::wstring print() const + + virtual const core::video_format_desc& get_video_format_desc() const { - return input_->print(); + return format_desc_; } -}; +}; -decklink_consumer::decklink_consumer(size_t device_index, bool embed_audio, bool internalKey) : impl_(new implementation(device_index, embed_audio, internalKey)){} -decklink_consumer::decklink_consumer(decklink_consumer&& other) : impl_(std::move(other.impl_)){} -void decklink_consumer::initialize(const core::video_format_desc& format_desc){impl_->initialize(format_desc);} -void decklink_consumer::send(const safe_ptr& frame){impl_->send(frame);} -size_t decklink_consumer::buffer_depth() const{return impl_->buffer_depth();} -std::wstring decklink_consumer::print() const{return impl_->print();} - -safe_ptr create_decklink_consumer(const std::vector& params) +safe_ptr create_decklink_consumer(const std::vector& params) { if(params.size() < 1 || params[0] != L"DECKLINK") return core::frame_consumer::empty(); - int device_index = 1; - bool embed_audio = false; - bool internal_key = false; + configuration config; + + if(params.size() > 1) + config.device_index = lexical_cast_or_default(params[1], config.device_index); + + config.internal_key = std::find(params.begin(), params.end(), L"INTERNAL_KEY") != params.end(); + config.low_latency = std::find(params.begin(), params.end(), L"LOW_LATENCY") != params.end(); + config.embedded_audio = std::find(params.begin(), params.end(), L"EMBEDDED_AUDIO") != params.end(); + config.key_only = std::find(params.begin(), params.end(), L"KEY_ONLY") != params.end(); - if(params.size() > 1) - device_index = lexical_cast_or_default(params[2], device_index); + return make_safe(config); +} - if(params.size() > 2) - embed_audio = lexical_cast_or_default(params[3], embed_audio); - - if(params.size() > 3) - internal_key = lexical_cast_or_default(params[4], internal_key); +safe_ptr create_decklink_consumer(const boost::property_tree::ptree& ptree) +{ + configuration config; + + config.internal_key = ptree.get("internal-key", config.internal_key); + config.low_latency = ptree.get("low-latency", config.low_latency); + config.key_only = ptree.get("key-only", config.key_only); + config.device_index = ptree.get("device", config.device_index); + config.embedded_audio = ptree.get("embedded-audio", config.embedded_audio); - return make_safe(device_index, embed_audio, internal_key); + return make_safe(config); } -} \ No newline at end of file +} + +/* +############################################################################## +Pre-rolling + +Mail: 2011-05-09 + +Yoshan +BMD Developer Support +developer@blackmagic-design.com + +----------------------------------------------------------------------------- + +Thanks for your inquiry. The minimum number of frames that you can preroll +for scheduled playback is three frames for video and four frames for audio. +As you mentioned if you preroll less frames then playback will not start or +playback will be very sporadic. From our experience with Media Express, we +recommended that at least seven frames are prerolled for smooth playback. + +Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag: +There can be around 3 frames worth of latency on scheduled output. +When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is +reduced or removed for scheduled playback. If the DisplayVideoFrameSync() +method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will +guarantee that the provided frame will be output as soon the previous +frame output has been completed. +################################################################################ +*/ + +/* +############################################################################## +Async DMA Transfer without redundant copying + +Mail: 2011-05-10 + +Yoshan +BMD Developer Support +developer@blackmagic-design.com + +----------------------------------------------------------------------------- + +Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame +and providing a pointer to your video buffer when GetBytes() is called. +This may help to keep copying to a minimum. Please ensure that the pixel +format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will +have to colourspace convert which may result in additional copying. +################################################################################ +*/ \ No newline at end of file