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/frame/audio_channel_layout.h>
32 #include <core/mixer/audio/audio_mixer.h>
33 #include <core/consumer/frame_consumer.h>
34 #include <core/diagnostics/call_context.h>
35 #include <core/help/help_sink.h>
36 #include <core/help/help_repository.h>
38 #include <common/executor.h>
39 #include <common/lock.h>
40 #include <common/diagnostics/graph.h>
41 #include <common/except.h>
42 #include <common/memshfl.h>
43 #include <common/array.h>
44 #include <common/future.h>
45 #include <common/cache_aligned_vector.h>
46 #include <common/timer.h>
47 #include <common/param.h>
49 #include <tbb/concurrent_queue.h>
51 #include <common/assert.h>
52 #include <boost/lexical_cast.hpp>
53 #include <boost/circular_buffer.hpp>
54 #include <boost/property_tree/ptree.hpp>
56 namespace caspar { namespace decklink {
64 external_separate_device_keyer,
65 default_keyer = external_keyer
72 default_latency = normal_latency
76 int key_device_idx = 0;
77 bool embedded_audio = true;
78 keyer_t keyer = keyer_t::default_keyer;
79 latency_t latency = latency_t::default_latency;
80 bool key_only = false;
81 int base_buffer_depth = 3;
82 core::audio_channel_layout out_channel_layout = core::audio_channel_layout::invalid();
84 int buffer_depth() const
86 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
89 int key_device_index() const
91 return key_device_idx == 0 ? device_index + 1 : key_device_idx;
94 core::audio_channel_layout get_adjusted_layout(const core::audio_channel_layout& in_layout) const
96 auto adjusted = out_channel_layout == core::audio_channel_layout::invalid() ? in_layout : out_channel_layout;
98 if (adjusted.num_channels == 1) // Duplicate mono-signal into both left and right.
100 adjusted.num_channels = 2;
101 adjusted.channel_order.push_back(adjusted.channel_order.at(0)); // Usually FC -> FC FC
103 else if (adjusted.num_channels == 2)
105 adjusted.num_channels = 2;
107 else if (adjusted.num_channels <= 8)
109 adjusted.num_channels = 8;
111 else // Over 8 always pad to 16 or drop >16
113 adjusted.num_channels = 16;
121 const com_iface_ptr<IDeckLinkConfiguration>& config,
122 configuration::latency_t latency,
123 const std::wstring& print)
125 if (latency == configuration::latency_t::low_latency)
127 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
128 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";
130 else if (latency == configuration::latency_t::normal_latency)
132 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
133 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";
138 const com_iface_ptr<IDeckLinkAttributes>& attributes,
139 const com_iface_ptr<IDeckLinkKeyer>& decklink_keyer,
140 configuration::keyer_t keyer,
141 const std::wstring& print)
143 if (keyer == configuration::keyer_t::internal_keyer)
146 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
147 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
148 else if (FAILED(decklink_keyer->Enable(FALSE)))
149 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
150 else if (FAILED(decklink_keyer->SetLevel(255)))
151 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
153 CASPAR_LOG(info) << print << L" Enabled internal keyer.";
155 else if (keyer == configuration::keyer_t::external_keyer)
158 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
159 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
160 else if (FAILED(decklink_keyer->Enable(TRUE)))
161 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
162 else if (FAILED(decklink_keyer->SetLevel(255)))
163 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
165 CASPAR_LOG(info) << print << L" Enabled external keyer.";
169 class decklink_frame : public IDeckLinkVideoFrame
171 tbb::atomic<int> ref_count_;
172 core::const_frame frame_;
173 const core::video_format_desc format_desc_;
175 const bool key_only_;
176 cache_aligned_vector<uint8_t> data_;
178 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
180 , format_desc_(format_desc)
181 , key_only_(key_only)
188 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
190 return E_NOINTERFACE;
193 virtual ULONG STDMETHODCALLTYPE AddRef()
198 virtual ULONG STDMETHODCALLTYPE Release()
200 if(--ref_count_ == 0)
205 // IDecklinkVideoFrame
207 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
208 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
209 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
210 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
211 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
213 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
217 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
219 data_.resize(format_desc_.size, 0);
220 *buffer = data_.data();
226 data_.resize(frame_.image_data().size());
227 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
229 *buffer = data_.data();
232 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
236 CASPAR_LOG_CURRENT_EXCEPTION();
243 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
244 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
248 const core::audio_buffer& audio_data()
250 return frame_.audio_data();
253 int64_t get_age_millis() const
255 return frame_.get_age_millis();
259 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
261 const configuration config_;
262 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
263 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
264 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
265 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
266 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
267 tbb::atomic<int64_t> current_presentation_delay_;
268 tbb::atomic<int64_t> scheduled_frames_completed_;
270 key_video_context(const configuration& config, const std::wstring& print)
273 current_presentation_delay_ = 0;
274 scheduled_frames_completed_ = 0;
276 set_latency(configuration_, config.latency, print);
277 set_keyer(attributes_, keyer_, config.keyer, print);
279 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
280 CASPAR_THROW_EXCEPTION(caspar_exception()
281 << msg_info(u8(print) + " Failed to set key playback completion callback.")
282 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
285 template<typename Print>
286 void enable_video(BMDDisplayMode display_mode, const Print& print)
288 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
289 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable key video output."));
291 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
292 CASPAR_THROW_EXCEPTION(caspar_exception()
293 << msg_info(u8(print()) + " Failed to set key playback completion callback.")
294 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
297 virtual ~key_video_context()
301 output_->StopScheduledPlayback(0, nullptr, 0);
302 output_->DisableVideoOutput();
306 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
307 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
308 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
310 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
315 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
316 IDeckLinkVideoFrame* completed_frame,
317 BMDOutputFrameCompletionResult result)
319 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
320 current_presentation_delay_ = dframe->get_age_millis();
321 ++scheduled_frames_completed_;
323 // Let the fill callback keep the pace, so no scheduling here.
329 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
331 const int channel_index_;
332 const configuration config_;
334 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
335 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
336 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
337 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
338 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
340 tbb::spin_mutex exception_mutex_;
341 std::exception_ptr exception_;
343 tbb::atomic<bool> is_running_;
345 const std::wstring model_name_ = get_model_name(decklink_);
346 const core::video_format_desc format_desc_;
347 const core::audio_channel_layout in_channel_layout_;
348 const core::audio_channel_layout out_channel_layout_ = config_.get_adjusted_layout(in_channel_layout_);
349 core::audio_channel_remapper channel_remapper_ { in_channel_layout_, out_channel_layout_ };
350 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
352 long long video_scheduled_ = 0;
353 long long audio_scheduled_ = 0;
355 int preroll_count_ = 0;
357 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
359 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
360 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
362 spl::shared_ptr<diagnostics::graph> graph_;
363 tbb::atomic<int64_t> current_presentation_delay_;
364 caspar::timer tick_timer_;
365 retry_task<bool> send_completion_;
366 tbb::atomic<int64_t> scheduled_frames_completed_;
367 std::unique_ptr<key_video_context> key_context_;
371 const configuration& config,
372 const core::video_format_desc& format_desc,
373 const core::audio_channel_layout& in_channel_layout,
375 : channel_index_(channel_index)
377 , format_desc_(format_desc)
378 , in_channel_layout_(in_channel_layout)
381 current_presentation_delay_ = 0;
382 scheduled_frames_completed_ = 0;
384 video_frame_buffer_.set_capacity(1);
386 // Blackmagic calls RenderAudioSamples() 50 times per second
387 // regardless of video mode so we sometimes need to give them
388 // samples from 2 frames in order to keep up
389 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
391 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
392 key_context_.reset(new key_video_context(config, print()));
394 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
395 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
396 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
397 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
398 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
399 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
403 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
406 graph_->set_text(print());
407 diagnostics::register_graph(graph_);
409 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
411 if(config.embedded_audio)
414 set_latency(configuration_, config.latency, print());
415 set_keyer(attributes_, keyer_, config.keyer, print());
417 if(config.embedded_audio)
418 output_->BeginAudioPreroll();
420 for(int n = 0; n < buffer_size_; ++n)
421 schedule_next_video(core::const_frame::empty());
423 if(!config.embedded_audio)
430 video_frame_buffer_.try_push(core::const_frame::empty());
431 audio_frame_buffer_.try_push(core::const_frame::empty());
433 if(output_ != nullptr)
435 output_->StopScheduledPlayback(0, nullptr, 0);
436 if(config_.embedded_audio)
437 output_->DisableAudioOutput();
438 output_->DisableVideoOutput();
444 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
445 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable audio output."));
447 if(FAILED(output_->SetAudioCallback(this)))
448 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not set audio callback."));
450 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
453 void enable_video(BMDDisplayMode display_mode)
455 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
456 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable fill video output."));
458 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
459 CASPAR_THROW_EXCEPTION(caspar_exception()
460 << msg_info(u8(print()) + " Failed to set fill playback completion callback.")
461 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
464 key_context_->enable_video(display_mode, [this]() { return print(); });
467 void start_playback()
469 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
470 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule fill playback."));
472 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
473 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule key playback."));
476 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
477 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
478 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
480 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
483 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
487 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
494 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
495 current_presentation_delay_ = dframe->get_age_millis();
496 ++scheduled_frames_completed_;
502 scheduled_frames_completed_
503 - key_context_->scheduled_frames_completed_)
506 if(result == bmdOutputFrameDisplayedLate)
508 graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
509 video_scheduled_ += format_desc_.duration;
510 audio_scheduled_ += dframe->audio_data().size() / out_channel_layout_.num_channels;
511 //++video_scheduled_;
512 //audio_scheduled_ += format_desc_.audio_cadence[0];
513 //++audio_scheduled_;
515 else if(result == bmdOutputFrameDropped)
516 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
517 else if(result == bmdOutputFrameFlushed)
518 graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
521 output_->GetBufferedVideoFrameCount(&buffered);
522 graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
524 auto frame = core::const_frame::empty();
525 video_frame_buffer_.pop(frame);
526 send_completion_.try_completion();
527 schedule_next_video(frame);
531 lock(exception_mutex_, [&]
533 exception_ = std::current_exception();
541 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
550 if(++preroll_count_ >= buffer_size_)
552 output_->EndAudioPreroll();
557 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
562 auto frame = core::const_frame::empty();
564 while(audio_frame_buffer_.try_pop(frame))
567 output_->GetBufferedAudioSampleFrameCount(&buffered);
568 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
570 send_completion_.try_completion();
571 schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
577 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
578 exception_ = std::current_exception();
586 void schedule_next_audio(const T& audio_data)
588 auto sample_frame_count = static_cast<int>(audio_data.size()/out_channel_layout_.num_channels);
590 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
592 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
593 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
595 audio_scheduled_ += sample_frame_count;
598 void schedule_next_video(core::const_frame frame)
602 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true));
603 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
604 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
607 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only));
608 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
609 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
611 video_scheduled_ += format_desc_.duration;
613 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
614 tick_timer_.restart();
617 std::future<bool> send(core::const_frame frame)
619 auto exception = lock(exception_mutex_, [&]
624 if(exception != nullptr)
625 std::rethrow_exception(exception);
628 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Is not running."));
630 bool audio_ready = !config_.embedded_audio;
631 bool video_ready = false;
633 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
636 audio_ready = audio_frame_buffer_.try_push(frame);
639 video_ready = video_frame_buffer_.try_push(frame);
641 if (audio_ready && video_ready)
644 return boost::optional<bool>();
648 return make_ready_future(true);
650 send_completion_.set_task(enqueue_task);
652 return send_completion_.get_future();
655 std::wstring print() const
657 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
658 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
659 boost::lexical_cast<std::wstring>(config_.device_index) +
661 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
663 format_desc_.name + L"]";
665 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
666 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
670 struct decklink_consumer_proxy : public core::frame_consumer
672 core::monitor::subject monitor_subject_;
673 const configuration config_;
674 std::unique_ptr<decklink_consumer> consumer_;
675 core::video_format_desc format_desc_;
679 decklink_consumer_proxy(const configuration& config)
681 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
683 auto ctx = core::diagnostics::call_context::for_thread();
684 executor_.begin_invoke([=]
686 core::diagnostics::call_context::for_thread() = ctx;
691 ~decklink_consumer_proxy()
702 void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
704 format_desc_ = format_desc;
708 consumer_.reset(new decklink_consumer(config_, format_desc, channel_layout, channel_index));
712 std::future<bool> send(core::const_frame frame) override
714 return consumer_->send(frame);
717 std::wstring print() const override
719 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
722 std::wstring name() const override
727 boost::property_tree::wptree info() const override
729 boost::property_tree::wptree info;
730 info.add(L"type", L"decklink");
731 info.add(L"key-only", config_.key_only);
732 info.add(L"device", config_.device_index);
734 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
736 info.add(L"key-device", config_.key_device_index());
739 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
740 info.add(L"embedded-audio", config_.embedded_audio);
741 info.add(L"presentation-frame-age", presentation_frame_age_millis());
742 //info.add(L"internal-key", config_.internal_key);
746 int buffer_depth() const override
748 return config_.buffer_depth() + 2;
751 int index() const override
753 return 300 + config_.device_index;
756 int64_t presentation_frame_age_millis() const override
758 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
761 core::monitor::subject& monitor_output()
763 return monitor_subject_;
767 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
769 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
770 sink.syntax(L"DECKLINK "
771 L"{[device_index:int]|1} "
772 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
773 L"{[low_latency:LOW_LATENCY]} "
774 L"{[embedded_audio:EMBEDDED_AUDIO]} "
775 L"{[key_only:KEY_ONLY]} "
776 L"{CHANNEL_LAYOUT [channel_layout:string]}");
777 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
779 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
781 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
782 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
783 ->item(L"low_latency", L"Tries to enable low latency if given.")
784 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
786 L" will extract only the alpha channel from the "
787 L"channel. This is useful when you have two SDI video cards, and neither has native support "
788 L"for separate fill/key output")
789 ->item(L"channel_layout", L"If specified, overrides the audio channel layout used by the channel.");
790 sink.para()->text(L"Examples:");
791 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
792 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
793 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
795 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
796 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.");
798 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
799 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
802 spl::shared_ptr<core::frame_consumer> create_consumer(
803 const std::vector<std::wstring>& params, core::interaction_sink*)
805 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
806 return core::frame_consumer::empty();
808 configuration config;
810 if (params.size() > 1)
811 config.device_index = boost::lexical_cast<int>(params.at(1));
813 if (contains_param(L"INTERNAL_KEY", params))
814 config.keyer = configuration::keyer_t::internal_keyer;
815 else if (contains_param(L"EXTERNAL_KEY", params))
816 config.keyer = configuration::keyer_t::external_keyer;
817 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
818 config.keyer = configuration::keyer_t::external_separate_device_keyer;
820 config.keyer = configuration::keyer_t::default_keyer;
822 if (contains_param(L"LOW_LATENCY", params))
823 config.latency = configuration::latency_t::low_latency;
825 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
826 config.key_only = contains_param(L"KEY_ONLY", params);
828 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params);
830 if (!channel_layout.empty())
832 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
835 CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"Channel layout " + channel_layout + L" not found."));
837 config.out_channel_layout = *found_layout;
840 return spl::make_shared<decklink_consumer_proxy>(config);
843 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
844 const boost::property_tree::wptree& ptree, core::interaction_sink*)
846 configuration config;
848 auto keyer = ptree.get(L"keyer", L"default");
849 if(keyer == L"external")
850 config.keyer = configuration::keyer_t::external_keyer;
851 else if(keyer == L"internal")
852 config.keyer = configuration::keyer_t::internal_keyer;
853 else if (keyer == L"external_separate_device")
854 config.keyer = configuration::keyer_t::external_separate_device_keyer;
856 auto latency = ptree.get(L"latency", L"default");
857 if(latency == L"low")
858 config.latency = configuration::latency_t::low_latency;
859 else if(latency == L"normal")
860 config.latency = configuration::latency_t::normal_latency;
862 auto channel_layout = ptree.get_optional<std::wstring>(L"channel-layout");
866 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
869 CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
871 config.out_channel_layout = *found_layout;
874 config.key_only = ptree.get(L"key-only", config.key_only);
875 config.device_index = ptree.get(L"device", config.device_index);
876 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
877 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
878 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
880 return spl::make_shared<decklink_consumer_proxy>(config);
886 ##############################################################################
892 BMD Developer Support
893 developer@blackmagic-design.com
895 -----------------------------------------------------------------------------
897 Thanks for your inquiry. The minimum number of frames that you can preroll
898 for scheduled playback is three frames for video and four frames for audio.
899 As you mentioned if you preroll less frames then playback will not start or
900 playback will be very sporadic. From our experience with Media Express, we
901 recommended that at least seven frames are prerolled for smooth playback.
903 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
904 There can be around 3 frames worth of latency on scheduled output.
905 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
906 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
907 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
908 guarantee that the provided frame will be output as soon the previous
909 frame output has been completed.
910 ################################################################################
914 ##############################################################################
915 Async DMA Transfer without redundant copying
920 BMD Developer Support
921 developer@blackmagic-design.com
923 -----------------------------------------------------------------------------
925 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
926 and providing a pointer to your video buffer when GetBytes() is called.
927 This may help to keep copying to a minimum. Please ensure that the pixel
928 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
929 have to colourspace convert which may result in additional copying.
930 ################################################################################