2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
19 * Author: Robert Nagy, ronag89@gmail.com
22 #include "../StdAfx.h"
24 #include "decklink_consumer.h"
26 #include "../util/util.h"
28 #include "../decklink_api.h"
30 #include <core/frame/frame.h>
31 #include <core/mixer/audio/audio_mixer.h>
32 #include <core/consumer/frame_consumer.h>
33 #include <core/diagnostics/call_context.h>
34 #include <core/help/help_sink.h>
35 #include <core/help/help_repository.h>
37 #include <common/executor.h>
38 #include <common/lock.h>
39 #include <common/diagnostics/graph.h>
40 #include <common/except.h>
41 #include <common/memshfl.h>
42 #include <common/array.h>
43 #include <common/future.h>
44 #include <common/cache_aligned_vector.h>
45 #include <common/timer.h>
46 #include <common/param.h>
48 #include <tbb/concurrent_queue.h>
50 #include <common/assert.h>
51 #include <boost/lexical_cast.hpp>
52 #include <boost/circular_buffer.hpp>
53 #include <boost/property_tree/ptree.hpp>
55 namespace caspar { namespace decklink {
63 external_separate_device_keyer,
75 int key_device_idx = 0;
76 bool embedded_audio = true;
77 keyer_t keyer = keyer_t::default_keyer;
78 latency_t latency = latency_t::default_latency;
79 bool key_only = false;
80 int base_buffer_depth = 3;
82 int buffer_depth() const
84 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
87 int key_device_index() const
89 return key_device_idx == 0 ? device_index + 1 : key_device_idx;
94 const com_iface_ptr<IDeckLinkConfiguration>& config,
95 configuration::latency_t latency,
96 const std::wstring& print)
98 if (latency == configuration::latency_t::low_latency)
100 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
101 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";
103 else if (latency == configuration::latency_t::normal_latency)
105 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
106 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";
111 const com_iface_ptr<IDeckLinkAttributes>& attributes,
112 const com_iface_ptr<IDeckLinkKeyer>& decklink_keyer,
113 configuration::keyer_t keyer,
114 const std::wstring& print)
116 if (keyer == configuration::keyer_t::internal_keyer
117 || keyer == configuration::keyer_t::external_separate_device_keyer)
120 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
121 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
122 else if (FAILED(decklink_keyer->Enable(FALSE)))
123 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
124 else if (FAILED(decklink_keyer->SetLevel(255)))
125 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
127 CASPAR_LOG(info) << print << L" Enabled internal keyer.";
129 else if (keyer == configuration::keyer_t::external_keyer)
132 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
133 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
134 else if (FAILED(decklink_keyer->Enable(TRUE)))
135 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
136 else if (FAILED(decklink_keyer->SetLevel(255)))
137 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
139 CASPAR_LOG(info) << print << L" Enabled external keyer.";
143 class decklink_frame : public IDeckLinkVideoFrame
145 tbb::atomic<int> ref_count_;
146 core::const_frame frame_;
147 const core::video_format_desc format_desc_;
149 const bool key_only_;
150 cache_aligned_vector<uint8_t> data_;
152 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
154 , format_desc_(format_desc)
155 , key_only_(key_only)
162 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
164 return E_NOINTERFACE;
167 virtual ULONG STDMETHODCALLTYPE AddRef()
172 virtual ULONG STDMETHODCALLTYPE Release()
174 if(--ref_count_ == 0)
179 // IDecklinkVideoFrame
181 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
182 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
183 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
184 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
185 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
187 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
191 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
193 data_.resize(format_desc_.size, 0);
194 *buffer = data_.data();
200 data_.resize(frame_.image_data().size());
201 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
203 *buffer = data_.data();
206 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
210 CASPAR_LOG_CURRENT_EXCEPTION();
217 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
218 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
222 const core::audio_buffer& audio_data()
224 return frame_.audio_data();
227 int64_t get_age_millis() const
229 return frame_.get_age_millis();
233 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
235 const configuration config_;
236 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
237 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
238 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
239 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
240 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
241 tbb::atomic<int64_t> current_presentation_delay_;
242 tbb::atomic<int64_t> scheduled_frames_completed_;
244 key_video_context(const configuration& config, const std::wstring& print)
248 , attributes_(decklink_)
249 , configuration_(decklink_)
251 current_presentation_delay_ = 0;
252 scheduled_frames_completed_ = 0;
254 set_latency(configuration_, config.latency, print);
255 set_keyer(attributes_, keyer_, config.keyer, print);
257 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
258 CASPAR_THROW_EXCEPTION(caspar_exception()
259 << msg_info(u8(print) + " Failed to set key playback completion callback.")
260 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
263 template<typename Print>
264 void enable_video(BMDDisplayMode display_mode, const Print& print)
266 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
267 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable key video output."));
269 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
270 CASPAR_THROW_EXCEPTION(caspar_exception()
271 << msg_info(u8(print()) + " Failed to set key playback completion callback.")
272 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
275 virtual ~key_video_context()
279 output_->StopScheduledPlayback(0, nullptr, 0);
280 output_->DisableVideoOutput();
284 STDMETHOD(QueryInterface(REFIID, LPVOID*)) { return E_NOINTERFACE; }
285 STDMETHOD_(ULONG, AddRef()) { return 1; }
286 STDMETHOD_(ULONG, Release()) { return 1; }
288 STDMETHOD(ScheduledPlaybackHasStopped())
293 STDMETHOD(ScheduledFrameCompleted(
294 IDeckLinkVideoFrame* completed_frame,
295 BMDOutputFrameCompletionResult result))
297 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
298 current_presentation_delay_ = dframe->get_age_millis();
299 ++scheduled_frames_completed_;
301 // Let the fill callback keep the pace, so no scheduling here.
307 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
309 const int channel_index_;
310 const configuration config_;
312 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
313 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
314 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
315 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
316 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
318 tbb::spin_mutex exception_mutex_;
319 std::exception_ptr exception_;
321 tbb::atomic<bool> is_running_;
323 const std::wstring model_name_ = get_model_name(decklink_);
324 const core::video_format_desc format_desc_;
325 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
327 long long video_scheduled_ = 0;
328 long long audio_scheduled_ = 0;
330 int preroll_count_ = 0;
332 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
334 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
335 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
337 spl::shared_ptr<diagnostics::graph> graph_;
338 tbb::atomic<int64_t> current_presentation_delay_;
339 caspar::timer tick_timer_;
340 retry_task<bool> send_completion_;
341 tbb::atomic<int64_t> scheduled_frames_completed_;
342 std::unique_ptr<key_video_context> key_context_;
345 decklink_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index)
346 : channel_index_(channel_index)
348 , format_desc_(format_desc)
351 current_presentation_delay_ = 0;
352 scheduled_frames_completed_ = 0;
354 video_frame_buffer_.set_capacity(1);
356 // Blackmagic calls RenderAudioSamples() 50 times per second
357 // regardless of video mode so we sometimes need to give them
358 // samples from 2 frames in order to keep up
359 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
361 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
362 key_context_.reset(new key_video_context(config, print()));
364 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
365 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
366 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
367 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
368 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
369 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
373 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
376 graph_->set_text(print());
377 diagnostics::register_graph(graph_);
379 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
381 if(config.embedded_audio)
384 set_latency(configuration_, config.latency, print());
385 set_keyer(attributes_, keyer_, config.keyer, print());
387 if(config.embedded_audio)
388 output_->BeginAudioPreroll();
390 for(int n = 0; n < buffer_size_; ++n)
391 schedule_next_video(core::const_frame::empty());
393 if(!config.embedded_audio)
400 video_frame_buffer_.try_push(core::const_frame::empty());
401 audio_frame_buffer_.try_push(core::const_frame::empty());
403 if(output_ != nullptr)
405 output_->StopScheduledPlayback(0, nullptr, 0);
406 if(config_.embedded_audio)
407 output_->DisableAudioOutput();
408 output_->DisableVideoOutput();
414 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))
415 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable audio output."));
417 if(FAILED(output_->SetAudioCallback(this)))
418 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not set audio callback."));
420 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
423 void enable_video(BMDDisplayMode display_mode)
425 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
426 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable fill video output."));
428 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
429 CASPAR_THROW_EXCEPTION(caspar_exception()
430 << msg_info(u8(print()) + " Failed to set fill playback completion callback.")
431 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
434 key_context_->enable_video(display_mode, [this]() { return print(); });
437 void start_playback()
439 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
440 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule fill playback."));
442 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
443 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule key playback."));
446 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
447 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
448 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
450 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
453 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
457 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
464 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
465 current_presentation_delay_ = dframe->get_age_millis();
466 ++scheduled_frames_completed_;
472 scheduled_frames_completed_
473 - key_context_->scheduled_frames_completed_)
476 if(result == bmdOutputFrameDisplayedLate)
478 graph_->set_tag("late-frame");
479 video_scheduled_ += format_desc_.duration;
480 audio_scheduled_ += dframe->audio_data().size() / format_desc_.audio_channels;
481 //++video_scheduled_;
482 //audio_scheduled_ += format_desc_.audio_cadence[0];
483 //++audio_scheduled_;
485 else if(result == bmdOutputFrameDropped)
486 graph_->set_tag("dropped-frame");
487 else if(result == bmdOutputFrameFlushed)
488 graph_->set_tag("flushed-frame");
490 auto frame = core::const_frame::empty();
491 video_frame_buffer_.pop(frame);
492 send_completion_.try_completion();
493 schedule_next_video(frame);
496 output_->GetBufferedVideoFrameCount(&buffered);
497 graph_->set_value("buffered-video", static_cast<double>(buffered)/format_desc_.fps);
501 lock(exception_mutex_, [&]
503 exception_ = std::current_exception();
511 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
520 if(++preroll_count_ >= buffer_size_)
522 output_->EndAudioPreroll();
527 schedule_next_audio(core::audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * format_desc_.audio_channels, 0));
532 auto frame = core::const_frame::empty();
534 while(audio_frame_buffer_.try_pop(frame))
536 send_completion_.try_completion();
537 schedule_next_audio(frame.audio_data());
542 output_->GetBufferedAudioSampleFrameCount(&buffered);
543 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * format_desc_.audio_channels * 2));
547 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
548 exception_ = std::current_exception();
556 void schedule_next_audio(const T& audio_data)
558 auto sample_frame_count = static_cast<int>(audio_data.size()/format_desc_.audio_channels);
560 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
562 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
563 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
565 audio_scheduled_ += sample_frame_count;
568 void schedule_next_video(core::const_frame frame)
572 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true));
573 if (FAILED(key_context_->output_->ScheduleVideoFrame(key_frame, video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
574 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
577 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only));
578 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
579 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
581 video_scheduled_ += format_desc_.duration;
583 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
584 tick_timer_.restart();
587 std::future<bool> send(core::const_frame frame)
589 auto exception = lock(exception_mutex_, [&]
594 if(exception != nullptr)
595 std::rethrow_exception(exception);
598 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Is not running."));
600 bool audio_ready = !config_.embedded_audio;
601 bool video_ready = false;
603 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
606 audio_ready = audio_frame_buffer_.try_push(frame);
609 video_ready = video_frame_buffer_.try_push(frame);
611 if (audio_ready && video_ready)
614 return boost::optional<bool>();
618 return make_ready_future(true);
620 send_completion_.set_task(enqueue_task);
622 return send_completion_.get_future();
625 std::wstring print() const
627 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
628 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
629 boost::lexical_cast<std::wstring>(config_.device_index) +
631 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
633 format_desc_.name + L"]";
635 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
636 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
640 struct decklink_consumer_proxy : public core::frame_consumer
642 core::monitor::subject monitor_subject_;
643 const configuration config_;
644 std::unique_ptr<decklink_consumer> consumer_;
645 core::video_format_desc format_desc_;
649 decklink_consumer_proxy(const configuration& config)
651 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
653 auto ctx = core::diagnostics::call_context::for_thread();
654 executor_.begin_invoke([=]
656 core::diagnostics::call_context::for_thread() = ctx;
661 ~decklink_consumer_proxy()
672 void initialize(const core::video_format_desc& format_desc, int channel_index) override
674 format_desc_ = format_desc;
678 consumer_.reset(new decklink_consumer(config_, format_desc, channel_index));
682 std::future<bool> send(core::const_frame frame) override
684 return consumer_->send(frame);
687 std::wstring print() const override
689 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
692 std::wstring name() const override
697 boost::property_tree::wptree info() const override
699 boost::property_tree::wptree info;
700 info.add(L"type", L"decklink");
701 info.add(L"key-only", config_.key_only);
702 info.add(L"device", config_.device_index);
704 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
706 info.add(L"key-device", config_.key_device_index());
709 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
710 info.add(L"embedded-audio", config_.embedded_audio);
711 info.add(L"presentation-frame-age", presentation_frame_age_millis());
712 //info.add(L"internal-key", config_.internal_key);
716 int buffer_depth() const override
718 return config_.buffer_depth() + 2;
721 int index() const override
723 return 300 + config_.device_index;
726 int64_t presentation_frame_age_millis() const override
728 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
731 core::monitor::subject& monitor_output()
733 return monitor_subject_;
737 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
739 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
740 sink.syntax(L"DECKLINK "
741 L"{[device_index:int]|1} "
742 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
743 L"{[low_latency:LOW_LATENCY]} "
744 L"{[embedded_audio:EMBEDDED_AUDIO]} "
745 L"{[key_only:KEY_ONLY]}");
746 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
748 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
750 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
751 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
752 ->item(L"low_latency", L"Tries to enable low latency if given.")
753 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
755 L" will extract only the alpha channel from the "
756 L"channel. This is useful when you have two SDI video cards, and neither has native support "
757 L"for separate fill/key output");
758 sink.para()->text(L"Examples:");
759 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
760 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
761 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
763 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
764 L">> ADD 1 DECKLINK 2 KEY_ONLY", L"uses device with index 1 as fill output with audio and device with index 2 as key output.");
766 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
767 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
770 spl::shared_ptr<core::frame_consumer> create_consumer(
771 const std::vector<std::wstring>& params, core::interaction_sink*)
773 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
774 return core::frame_consumer::empty();
776 configuration config;
778 if (params.size() > 1)
779 config.device_index = boost::lexical_cast<int>(params.at(1));
781 if (contains_param(L"INTERNAL_KEY", params))
782 config.keyer = configuration::keyer_t::internal_keyer;
783 else if (contains_param(L"EXTERNAL_KEY", params))
784 config.keyer = configuration::keyer_t::external_keyer;
785 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
786 config.keyer = configuration::keyer_t::external_separate_device_keyer;
788 config.keyer = configuration::keyer_t::default_keyer;
790 if (contains_param(L"LOW_LATENCY", params))
791 config.latency = configuration::latency_t::low_latency;
793 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
794 config.key_only = contains_param(L"KEY_ONLY", params);
796 return spl::make_shared<decklink_consumer_proxy>(config);
799 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
800 const boost::property_tree::wptree& ptree, core::interaction_sink*)
802 configuration config;
804 auto keyer = ptree.get(L"keyer", L"default");
805 if(keyer == L"external")
806 config.keyer = configuration::keyer_t::external_keyer;
807 else if(keyer == L"internal")
808 config.keyer = configuration::keyer_t::internal_keyer;
809 else if (keyer == L"external_separate_device")
810 config.keyer = configuration::keyer_t::external_separate_device_keyer;
812 auto latency = ptree.get(L"latency", L"normal");
813 if(latency == L"low")
814 config.latency = configuration::latency_t::low_latency;
815 else if(latency == L"normal")
816 config.latency = configuration::latency_t::normal_latency;
818 config.key_only = ptree.get(L"key-only", config.key_only);
819 config.device_index = ptree.get(L"device", config.device_index);
820 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
821 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
822 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
824 return spl::make_shared<decklink_consumer_proxy>(config);
830 ##############################################################################
836 BMD Developer Support
837 developer@blackmagic-design.com
839 -----------------------------------------------------------------------------
841 Thanks for your inquiry. The minimum number of frames that you can preroll
842 for scheduled playback is three frames for video and four frames for audio.
843 As you mentioned if you preroll less frames then playback will not start or
844 playback will be very sporadic. From our experience with Media Express, we
845 recommended that at least seven frames are prerolled for smooth playback.
847 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
848 There can be around 3 frames worth of latency on scheduled output.
849 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
850 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
851 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
852 guarantee that the provided frame will be output as soon the previous
853 frame output has been completed.
854 ################################################################################
858 ##############################################################################
859 Async DMA Transfer without redundant copying
864 BMD Developer Support
865 developer@blackmagic-design.com
867 -----------------------------------------------------------------------------
869 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
870 and providing a pointer to your video buffer when GetBytes() is called.
871 This may help to keep copying to a minimum. Please ensure that the pixel
872 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
873 have to colourspace convert which may result in additional copying.
874 ################################################################################