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)
119 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
120 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
121 else if (FAILED(decklink_keyer->Enable(FALSE)))
122 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
123 else if (FAILED(decklink_keyer->SetLevel(255)))
124 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
126 CASPAR_LOG(info) << print << L" Enabled internal keyer.";
128 else if (keyer == configuration::keyer_t::external_keyer)
131 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
132 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
133 else if (FAILED(decklink_keyer->Enable(TRUE)))
134 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
135 else if (FAILED(decklink_keyer->SetLevel(255)))
136 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
138 CASPAR_LOG(info) << print << L" Enabled external keyer.";
142 class decklink_frame : public IDeckLinkVideoFrame
144 tbb::atomic<int> ref_count_;
145 core::const_frame frame_;
146 const core::video_format_desc format_desc_;
148 const bool key_only_;
149 cache_aligned_vector<uint8_t> data_;
151 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
153 , format_desc_(format_desc)
154 , key_only_(key_only)
161 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
163 return E_NOINTERFACE;
166 virtual ULONG STDMETHODCALLTYPE AddRef()
171 virtual ULONG STDMETHODCALLTYPE Release()
173 if(--ref_count_ == 0)
178 // IDecklinkVideoFrame
180 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
181 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
182 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
183 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
184 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
186 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
190 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
192 data_.resize(format_desc_.size, 0);
193 *buffer = data_.data();
199 data_.resize(frame_.image_data().size());
200 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
202 *buffer = data_.data();
205 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
209 CASPAR_LOG_CURRENT_EXCEPTION();
216 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
217 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
221 const core::audio_buffer& audio_data()
223 return frame_.audio_data();
226 int64_t get_age_millis() const
228 return frame_.get_age_millis();
232 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
234 const configuration config_;
235 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
236 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
237 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
238 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
239 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
240 tbb::atomic<int64_t> current_presentation_delay_;
241 tbb::atomic<int64_t> scheduled_frames_completed_;
243 key_video_context(const configuration& config, const std::wstring& print)
246 current_presentation_delay_ = 0;
247 scheduled_frames_completed_ = 0;
249 set_latency(configuration_, config.latency, print);
250 set_keyer(attributes_, keyer_, config.keyer, print);
252 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
253 CASPAR_THROW_EXCEPTION(caspar_exception()
254 << msg_info(u8(print) + " Failed to set key playback completion callback.")
255 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
258 template<typename Print>
259 void enable_video(BMDDisplayMode display_mode, const Print& print)
261 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
262 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable key video output."));
264 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
265 CASPAR_THROW_EXCEPTION(caspar_exception()
266 << msg_info(u8(print()) + " Failed to set key playback completion callback.")
267 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
270 virtual ~key_video_context()
274 output_->StopScheduledPlayback(0, nullptr, 0);
275 output_->DisableVideoOutput();
279 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
280 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
281 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
283 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
288 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
289 IDeckLinkVideoFrame* completed_frame,
290 BMDOutputFrameCompletionResult result)
292 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
293 current_presentation_delay_ = dframe->get_age_millis();
294 ++scheduled_frames_completed_;
296 // Let the fill callback keep the pace, so no scheduling here.
302 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
304 const int channel_index_;
305 const configuration config_;
307 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
308 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
309 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
310 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
311 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
313 tbb::spin_mutex exception_mutex_;
314 std::exception_ptr exception_;
316 tbb::atomic<bool> is_running_;
318 const std::wstring model_name_ = get_model_name(decklink_);
319 const core::video_format_desc format_desc_;
320 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
322 long long video_scheduled_ = 0;
323 long long audio_scheduled_ = 0;
325 int preroll_count_ = 0;
327 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
329 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
330 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
332 spl::shared_ptr<diagnostics::graph> graph_;
333 tbb::atomic<int64_t> current_presentation_delay_;
334 caspar::timer tick_timer_;
335 retry_task<bool> send_completion_;
336 tbb::atomic<int64_t> scheduled_frames_completed_;
337 std::unique_ptr<key_video_context> key_context_;
340 decklink_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index)
341 : channel_index_(channel_index)
343 , format_desc_(format_desc)
346 current_presentation_delay_ = 0;
347 scheduled_frames_completed_ = 0;
349 video_frame_buffer_.set_capacity(1);
351 // Blackmagic calls RenderAudioSamples() 50 times per second
352 // regardless of video mode so we sometimes need to give them
353 // samples from 2 frames in order to keep up
354 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
356 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
357 key_context_.reset(new key_video_context(config, print()));
359 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
360 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
361 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
362 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
363 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
364 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
368 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
371 graph_->set_text(print());
372 diagnostics::register_graph(graph_);
374 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
376 if(config.embedded_audio)
379 set_latency(configuration_, config.latency, print());
380 set_keyer(attributes_, keyer_, config.keyer, print());
382 if(config.embedded_audio)
383 output_->BeginAudioPreroll();
385 for(int n = 0; n < buffer_size_; ++n)
386 schedule_next_video(core::const_frame::empty());
388 if(!config.embedded_audio)
395 video_frame_buffer_.try_push(core::const_frame::empty());
396 audio_frame_buffer_.try_push(core::const_frame::empty());
398 if(output_ != nullptr)
400 output_->StopScheduledPlayback(0, nullptr, 0);
401 if(config_.embedded_audio)
402 output_->DisableAudioOutput();
403 output_->DisableVideoOutput();
409 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))
410 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable audio output."));
412 if(FAILED(output_->SetAudioCallback(this)))
413 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not set audio callback."));
415 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
418 void enable_video(BMDDisplayMode display_mode)
420 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
421 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable fill video output."));
423 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
424 CASPAR_THROW_EXCEPTION(caspar_exception()
425 << msg_info(u8(print()) + " Failed to set fill playback completion callback.")
426 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
429 key_context_->enable_video(display_mode, [this]() { return print(); });
432 void start_playback()
434 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
435 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule fill playback."));
437 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
438 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule key playback."));
441 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
442 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
443 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
445 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
448 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
452 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
459 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
460 current_presentation_delay_ = dframe->get_age_millis();
461 ++scheduled_frames_completed_;
467 scheduled_frames_completed_
468 - key_context_->scheduled_frames_completed_)
471 if(result == bmdOutputFrameDisplayedLate)
473 graph_->set_tag("late-frame");
474 video_scheduled_ += format_desc_.duration;
475 audio_scheduled_ += dframe->audio_data().size() / format_desc_.audio_channels;
476 //++video_scheduled_;
477 //audio_scheduled_ += format_desc_.audio_cadence[0];
478 //++audio_scheduled_;
480 else if(result == bmdOutputFrameDropped)
481 graph_->set_tag("dropped-frame");
482 else if(result == bmdOutputFrameFlushed)
483 graph_->set_tag("flushed-frame");
485 auto frame = core::const_frame::empty();
486 video_frame_buffer_.pop(frame);
487 send_completion_.try_completion();
488 schedule_next_video(frame);
491 output_->GetBufferedVideoFrameCount(&buffered);
492 graph_->set_value("buffered-video", static_cast<double>(buffered)/format_desc_.fps);
496 lock(exception_mutex_, [&]
498 exception_ = std::current_exception();
506 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
515 if(++preroll_count_ >= buffer_size_)
517 output_->EndAudioPreroll();
522 schedule_next_audio(core::audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * format_desc_.audio_channels, 0));
527 auto frame = core::const_frame::empty();
529 while(audio_frame_buffer_.try_pop(frame))
531 send_completion_.try_completion();
532 schedule_next_audio(frame.audio_data());
537 output_->GetBufferedAudioSampleFrameCount(&buffered);
538 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * format_desc_.audio_channels * 2));
542 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
543 exception_ = std::current_exception();
551 void schedule_next_audio(const T& audio_data)
553 auto sample_frame_count = static_cast<int>(audio_data.size()/format_desc_.audio_channels);
555 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
557 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
558 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
560 audio_scheduled_ += sample_frame_count;
563 void schedule_next_video(core::const_frame frame)
567 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true));
568 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
569 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
572 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only));
573 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
574 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
576 video_scheduled_ += format_desc_.duration;
578 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
579 tick_timer_.restart();
582 std::future<bool> send(core::const_frame frame)
584 auto exception = lock(exception_mutex_, [&]
589 if(exception != nullptr)
590 std::rethrow_exception(exception);
593 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Is not running."));
595 bool audio_ready = !config_.embedded_audio;
596 bool video_ready = false;
598 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
601 audio_ready = audio_frame_buffer_.try_push(frame);
604 video_ready = video_frame_buffer_.try_push(frame);
606 if (audio_ready && video_ready)
609 return boost::optional<bool>();
613 return make_ready_future(true);
615 send_completion_.set_task(enqueue_task);
617 return send_completion_.get_future();
620 std::wstring print() const
622 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
623 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
624 boost::lexical_cast<std::wstring>(config_.device_index) +
626 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
628 format_desc_.name + L"]";
630 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
631 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
635 struct decklink_consumer_proxy : public core::frame_consumer
637 core::monitor::subject monitor_subject_;
638 const configuration config_;
639 std::unique_ptr<decklink_consumer> consumer_;
640 core::video_format_desc format_desc_;
644 decklink_consumer_proxy(const configuration& config)
646 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
648 auto ctx = core::diagnostics::call_context::for_thread();
649 executor_.begin_invoke([=]
651 core::diagnostics::call_context::for_thread() = ctx;
656 ~decklink_consumer_proxy()
667 void initialize(const core::video_format_desc& format_desc, int channel_index) override
669 format_desc_ = format_desc;
673 consumer_.reset(new decklink_consumer(config_, format_desc, channel_index));
677 std::future<bool> send(core::const_frame frame) override
679 return consumer_->send(frame);
682 std::wstring print() const override
684 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
687 std::wstring name() const override
692 boost::property_tree::wptree info() const override
694 boost::property_tree::wptree info;
695 info.add(L"type", L"decklink");
696 info.add(L"key-only", config_.key_only);
697 info.add(L"device", config_.device_index);
699 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
701 info.add(L"key-device", config_.key_device_index());
704 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
705 info.add(L"embedded-audio", config_.embedded_audio);
706 info.add(L"presentation-frame-age", presentation_frame_age_millis());
707 //info.add(L"internal-key", config_.internal_key);
711 int buffer_depth() const override
713 return config_.buffer_depth() + 2;
716 int index() const override
718 return 300 + config_.device_index;
721 int64_t presentation_frame_age_millis() const override
723 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
726 core::monitor::subject& monitor_output()
728 return monitor_subject_;
732 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
734 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
735 sink.syntax(L"DECKLINK "
736 L"{[device_index:int]|1} "
737 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
738 L"{[low_latency:LOW_LATENCY]} "
739 L"{[embedded_audio:EMBEDDED_AUDIO]} "
740 L"{[key_only:KEY_ONLY]}");
741 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
743 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
745 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
746 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
747 ->item(L"low_latency", L"Tries to enable low latency if given.")
748 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
750 L" will extract only the alpha channel from the "
751 L"channel. This is useful when you have two SDI video cards, and neither has native support "
752 L"for separate fill/key output");
753 sink.para()->text(L"Examples:");
754 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
755 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
756 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
758 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
759 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.");
761 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
762 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
765 spl::shared_ptr<core::frame_consumer> create_consumer(
766 const std::vector<std::wstring>& params, core::interaction_sink*)
768 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
769 return core::frame_consumer::empty();
771 configuration config;
773 if (params.size() > 1)
774 config.device_index = boost::lexical_cast<int>(params.at(1));
776 if (contains_param(L"INTERNAL_KEY", params))
777 config.keyer = configuration::keyer_t::internal_keyer;
778 else if (contains_param(L"EXTERNAL_KEY", params))
779 config.keyer = configuration::keyer_t::external_keyer;
780 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
781 config.keyer = configuration::keyer_t::external_separate_device_keyer;
783 config.keyer = configuration::keyer_t::default_keyer;
785 if (contains_param(L"LOW_LATENCY", params))
786 config.latency = configuration::latency_t::low_latency;
788 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
789 config.key_only = contains_param(L"KEY_ONLY", params);
791 return spl::make_shared<decklink_consumer_proxy>(config);
794 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
795 const boost::property_tree::wptree& ptree, core::interaction_sink*)
797 configuration config;
799 auto keyer = ptree.get(L"keyer", L"default");
800 if(keyer == L"external")
801 config.keyer = configuration::keyer_t::external_keyer;
802 else if(keyer == L"internal")
803 config.keyer = configuration::keyer_t::internal_keyer;
804 else if (keyer == L"external_separate_device")
805 config.keyer = configuration::keyer_t::external_separate_device_keyer;
807 auto latency = ptree.get(L"latency", L"normal");
808 if(latency == L"low")
809 config.latency = configuration::latency_t::low_latency;
810 else if(latency == L"normal")
811 config.latency = configuration::latency_t::normal_latency;
813 config.key_only = ptree.get(L"key-only", config.key_only);
814 config.device_index = ptree.get(L"device", config.device_index);
815 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
816 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
817 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
819 return spl::make_shared<decklink_consumer_proxy>(config);
825 ##############################################################################
831 BMD Developer Support
832 developer@blackmagic-design.com
834 -----------------------------------------------------------------------------
836 Thanks for your inquiry. The minimum number of frames that you can preroll
837 for scheduled playback is three frames for video and four frames for audio.
838 As you mentioned if you preroll less frames then playback will not start or
839 playback will be very sporadic. From our experience with Media Express, we
840 recommended that at least seven frames are prerolled for smooth playback.
842 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
843 There can be around 3 frames worth of latency on scheduled output.
844 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
845 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
846 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
847 guarantee that the provided frame will be output as soon the previous
848 frame output has been completed.
849 ################################################################################
853 ##############################################################################
854 Async DMA Transfer without redundant copying
859 BMD Developer Support
860 developer@blackmagic-design.com
862 -----------------------------------------------------------------------------
864 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
865 and providing a pointer to your video buffer when GetBytes() is called.
866 This may help to keep copying to a minimum. Please ensure that the pixel
867 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
868 have to colourspace convert which may result in additional copying.
869 ################################################################################