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"
27 #include "../decklink.h"
29 #include "../decklink_api.h"
31 #include <core/frame/frame.h>
32 #include <core/frame/audio_channel_layout.h>
33 #include <core/mixer/audio/audio_mixer.h>
34 #include <core/consumer/frame_consumer.h>
35 #include <core/diagnostics/call_context.h>
36 #include <core/help/help_sink.h>
37 #include <core/help/help_repository.h>
39 #include <common/executor.h>
40 #include <common/lock.h>
41 #include <common/diagnostics/graph.h>
42 #include <common/except.h>
43 #include <common/memshfl.h>
44 #include <common/memcpy.h>
45 #include <common/no_init_proxy.h>
46 #include <common/array.h>
47 #include <common/future.h>
48 #include <common/cache_aligned_vector.h>
49 #include <common/timer.h>
50 #include <common/param.h>
51 #include <common/software_version.h>
53 #include <tbb/concurrent_queue.h>
55 #include <common/assert.h>
56 #include <boost/lexical_cast.hpp>
57 #include <boost/circular_buffer.hpp>
58 #include <boost/property_tree/ptree.hpp>
62 namespace caspar { namespace decklink {
70 external_separate_device_keyer,
71 default_keyer = external_keyer
78 default_latency = normal_latency
82 int key_device_idx = 0;
83 bool embedded_audio = true;
84 keyer_t keyer = keyer_t::default_keyer;
85 latency_t latency = latency_t::default_latency;
86 bool key_only = false;
87 int base_buffer_depth = 3;
88 core::audio_channel_layout out_channel_layout = core::audio_channel_layout::invalid();
90 int buffer_depth() const
92 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1);
95 int key_device_index() const
97 return key_device_idx == 0 ? device_index + 1 : key_device_idx;
100 core::audio_channel_layout get_adjusted_layout(const core::audio_channel_layout& in_layout) const
102 auto adjusted = out_channel_layout == core::audio_channel_layout::invalid() ? in_layout : out_channel_layout;
104 if (adjusted.num_channels == 1) // Duplicate mono-signal into both left and right.
106 adjusted.num_channels = 2;
107 adjusted.channel_order.push_back(adjusted.channel_order.at(0)); // Usually FC -> FC FC
109 else if (adjusted.num_channels == 2)
111 adjusted.num_channels = 2;
113 else if (adjusted.num_channels <= 8)
115 adjusted.num_channels = 8;
117 else // Over 8 always pad to 16 or drop >16
119 adjusted.num_channels = 16;
126 template <typename Configuration>
128 const com_iface_ptr<Configuration>& config,
129 configuration::latency_t latency,
130 const std::wstring& print)
132 if (latency == configuration::latency_t::low_latency)
134 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
135 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";
137 else if (latency == configuration::latency_t::normal_latency)
139 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
140 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";
145 const com_iface_ptr<IDeckLinkAttributes>& attributes,
146 const com_iface_ptr<IDeckLinkKeyer>& decklink_keyer,
147 configuration::keyer_t keyer,
148 const std::wstring& print)
150 if (keyer == configuration::keyer_t::internal_keyer)
153 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
154 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
155 else if (FAILED(decklink_keyer->Enable(FALSE)))
156 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
157 else if (FAILED(decklink_keyer->SetLevel(255)))
158 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
160 CASPAR_LOG(info) << print << L" Enabled internal keyer.";
162 else if (keyer == configuration::keyer_t::external_keyer)
165 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
166 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
167 else if (FAILED(decklink_keyer->Enable(TRUE)))
168 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
169 else if (FAILED(decklink_keyer->SetLevel(255)))
170 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
172 CASPAR_LOG(info) << print << L" Enabled external keyer.";
176 class decklink_frame : public IDeckLinkVideoFrame
178 tbb::atomic<int> ref_count_;
179 core::const_frame frame_;
180 const core::video_format_desc format_desc_;
182 const bool key_only_;
184 cache_aligned_vector<no_init_proxy<uint8_t>> data_;
186 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only, bool will_attempt_dma)
188 , format_desc_(format_desc)
189 , key_only_(key_only)
193 bool dma_transfer_from_gl_buffer_impossible;
195 #if !defined(_MSC_VER)
196 // On Linux Decklink cannot DMA transfer from memory returned by glMapBuffer (at least on nvidia)
197 dma_transfer_from_gl_buffer_impossible = true;
199 // On Windows it is possible.
200 dma_transfer_from_gl_buffer_impossible = false;
203 needs_to_copy_ = will_attempt_dma && dma_transfer_from_gl_buffer_impossible;
208 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
210 return E_NOINTERFACE;
213 virtual ULONG STDMETHODCALLTYPE AddRef()
218 virtual ULONG STDMETHODCALLTYPE Release()
220 if(--ref_count_ == 0)
225 // IDecklinkVideoFrame
227 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
228 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
229 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
230 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
231 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
233 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
237 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
239 data_.resize(format_desc_.size);
240 *buffer = data_.data();
246 data_.resize(frame_.image_data().size());
247 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
249 *buffer = data_.data();
253 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
257 data_.resize(frame_.image_data().size());
258 fast_memcpy(data_.data(), *buffer, frame_.image_data().size());
259 *buffer = data_.data();
265 CASPAR_LOG_CURRENT_EXCEPTION();
272 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
273 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
277 const core::audio_buffer& audio_data()
279 return frame_.audio_data();
282 int64_t get_age_millis() const
284 return frame_.get_age_millis();
288 template <typename Configuration>
289 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
291 const configuration config_;
292 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
293 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
294 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
295 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
296 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
297 tbb::atomic<int64_t> current_presentation_delay_;
298 tbb::atomic<int64_t> scheduled_frames_completed_;
300 key_video_context(const configuration& config, const std::wstring& print)
303 current_presentation_delay_ = 0;
304 scheduled_frames_completed_ = 0;
306 set_latency(configuration_, config.latency, print);
307 set_keyer(attributes_, keyer_, config.keyer, print);
309 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
310 CASPAR_THROW_EXCEPTION(caspar_exception()
311 << msg_info(print + L" Failed to set key playback completion callback.")
312 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
315 template<typename Print>
316 void enable_video(BMDDisplayMode display_mode, const Print& print)
318 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
319 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable key video output."));
321 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
322 CASPAR_THROW_EXCEPTION(caspar_exception()
323 << msg_info(print() + L" Failed to set key playback completion callback.")
324 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
327 virtual ~key_video_context()
331 output_->StopScheduledPlayback(0, nullptr, 0);
332 output_->DisableVideoOutput();
336 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
337 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
338 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
340 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
345 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
346 IDeckLinkVideoFrame* completed_frame,
347 BMDOutputFrameCompletionResult result)
349 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
350 current_presentation_delay_ = dframe->get_age_millis();
351 ++scheduled_frames_completed_;
353 // Let the fill callback keep the pace, so no scheduling here.
359 template <typename Configuration>
360 struct decklink_consumer : public IDeckLinkVideoOutputCallback, boost::noncopyable
362 const int channel_index_;
363 const configuration config_;
365 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
366 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
367 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
368 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
369 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
371 tbb::spin_mutex exception_mutex_;
372 std::exception_ptr exception_;
374 tbb::atomic<bool> is_running_;
376 const std::wstring model_name_ = get_model_name(decklink_);
377 bool will_attempt_dma_;
378 const core::video_format_desc format_desc_;
379 const core::audio_channel_layout in_channel_layout_;
380 const core::audio_channel_layout out_channel_layout_ = config_.get_adjusted_layout(in_channel_layout_);
381 core::audio_channel_remapper channel_remapper_ { in_channel_layout_, out_channel_layout_ };
382 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
384 long long video_scheduled_ = 0;
385 long long audio_scheduled_ = 0;
387 int preroll_count_ = 0;
389 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
391 tbb::concurrent_bounded_queue<core::const_frame> frame_buffer_;
393 spl::shared_ptr<diagnostics::graph> graph_;
394 caspar::timer tick_timer_;
395 std::packaged_task<bool ()> send_completion_;
396 reference_signal_detector reference_signal_detector_ { output_ };
397 tbb::atomic<int64_t> current_presentation_delay_;
398 tbb::atomic<int64_t> scheduled_frames_completed_;
399 std::unique_ptr<key_video_context<Configuration>> key_context_;
403 const configuration& config,
404 const core::video_format_desc& format_desc,
405 const core::audio_channel_layout& in_channel_layout,
407 : channel_index_(channel_index)
409 , format_desc_(format_desc)
410 , in_channel_layout_(in_channel_layout)
413 current_presentation_delay_ = 0;
414 scheduled_frames_completed_ = 0;
416 frame_buffer_.set_capacity(1);
418 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
419 key_context_.reset(new key_video_context<Configuration>(config, print()));
421 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
422 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
423 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
424 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
425 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
426 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
430 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
433 graph_->set_text(print());
434 diagnostics::register_graph(graph_);
436 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, will_attempt_dma_));
438 if(config.embedded_audio)
441 set_latency(configuration_, config.latency, print());
442 set_keyer(attributes_, keyer_, config.keyer, print());
444 if(config.embedded_audio)
445 output_->BeginAudioPreroll();
447 for (int n = 0; n < buffer_size_; ++n)
449 if (config.embedded_audio)
450 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[n % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
452 schedule_next_video(core::const_frame::empty());
455 if (config.embedded_audio)
457 // Preroll one extra frame worth of audio
458 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[buffer_size_ % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
459 output_->EndAudioPreroll();
468 frame_buffer_.try_push(core::const_frame::empty());
470 if(output_ != nullptr)
472 output_->StopScheduledPlayback(0, nullptr, 0);
473 if(config_.embedded_audio)
474 output_->DisableAudioOutput();
475 output_->DisableVideoOutput();
481 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
482 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable audio output."));
484 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
487 void enable_video(BMDDisplayMode display_mode)
489 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
490 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable fill video output."));
492 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
493 CASPAR_THROW_EXCEPTION(caspar_exception()
494 << msg_info(print() + L" Failed to set fill playback completion callback.")
495 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
498 key_context_->enable_video(display_mode, [this]() { return print(); });
501 void start_playback()
503 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
504 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule fill playback."));
506 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
507 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule key playback."));
510 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
511 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
512 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
514 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
517 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
521 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
528 auto tick_time = tick_timer_.elapsed()*format_desc_.fps * 0.5;
529 graph_->set_value("tick-time", tick_time);
530 tick_timer_.restart();
532 reference_signal_detector_.detect_change([this]() { return print(); });
534 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
535 current_presentation_delay_ = dframe->get_age_millis();
536 ++scheduled_frames_completed_;
542 scheduled_frames_completed_
543 - key_context_->scheduled_frames_completed_)
546 if(result == bmdOutputFrameDisplayedLate)
548 graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
549 video_scheduled_ += format_desc_.duration;
550 audio_scheduled_ += dframe->audio_data().size() / in_channel_layout_.num_channels;
552 else if(result == bmdOutputFrameDropped)
553 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
554 else if(result == bmdOutputFrameFlushed)
555 graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
558 output_->GetBufferedVideoFrameCount(&buffered);
559 graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
561 if (config_.embedded_audio)
563 output_->GetBufferedAudioSampleFrameCount(&buffered);
564 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
567 auto frame = core::const_frame::empty();
569 frame_buffer_.pop(frame);
571 if (send_completion_.valid())
574 if (config_.embedded_audio)
575 schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
577 schedule_next_video(frame);
581 lock(exception_mutex_, [&]
583 exception_ = std::current_exception();
592 void schedule_next_audio(const T& audio_data)
594 auto sample_frame_count = static_cast<int>(audio_data.size()/out_channel_layout_.num_channels);
596 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
598 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
599 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
601 audio_scheduled_ += sample_frame_count;
604 void schedule_next_video(core::const_frame frame)
608 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true, will_attempt_dma_));
609 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
610 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
613 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only, will_attempt_dma_));
614 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
615 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
617 video_scheduled_ += format_desc_.duration;
620 std::future<bool> send(core::const_frame frame)
622 auto exception = lock(exception_mutex_, [&]
627 if(exception != nullptr)
628 std::rethrow_exception(exception);
631 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Is not running."));
633 if (frame_buffer_.try_push(frame))
634 return make_ready_future(true);
636 send_completion_ = std::packaged_task<bool ()>([frame, this] () mutable -> bool
638 frame_buffer_.push(frame);
643 return send_completion_.get_future();
646 std::wstring print() const
648 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
649 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
650 boost::lexical_cast<std::wstring>(config_.device_index) +
652 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
654 format_desc_.name + L"]";
656 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
657 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
661 template <typename Configuration>
662 struct decklink_consumer_proxy : public core::frame_consumer
664 core::monitor::subject monitor_subject_;
665 const configuration config_;
666 std::unique_ptr<decklink_consumer<Configuration>> consumer_;
667 core::video_format_desc format_desc_;
671 decklink_consumer_proxy(const configuration& config)
673 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
675 auto ctx = core::diagnostics::call_context::for_thread();
676 executor_.begin_invoke([=]
678 core::diagnostics::call_context::for_thread() = ctx;
683 ~decklink_consumer_proxy()
694 void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
696 format_desc_ = format_desc;
700 consumer_.reset(new decklink_consumer<Configuration>(config_, format_desc, channel_layout, channel_index));
704 std::future<bool> send(core::const_frame frame) override
706 return consumer_->send(frame);
709 std::wstring print() const override
711 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
714 std::wstring name() const override
719 boost::property_tree::wptree info() const override
721 boost::property_tree::wptree info;
722 info.add(L"type", L"decklink");
723 info.add(L"key-only", config_.key_only);
724 info.add(L"device", config_.device_index);
726 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
728 info.add(L"key-device", config_.key_device_index());
731 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
732 info.add(L"embedded-audio", config_.embedded_audio);
733 info.add(L"presentation-frame-age", presentation_frame_age_millis());
734 //info.add(L"internal-key", config_.internal_key);
738 int buffer_depth() const override
740 return config_.buffer_depth() + 2;
743 int index() const override
745 return 300 + config_.device_index;
748 int64_t presentation_frame_age_millis() const override
750 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
753 core::monitor::subject& monitor_output()
755 return monitor_subject_;
759 const software_version<3>& get_driver_version()
761 static software_version<3> version(u8(get_version()));
766 const software_version<3> get_new_configuration_api_version()
768 static software_version<3> NEW_CONFIGURATION_API("10.2");
770 return NEW_CONFIGURATION_API;
773 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
775 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
776 sink.syntax(L"DECKLINK "
777 L"{[device_index:int]|1} "
778 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
779 L"{[low_latency:LOW_LATENCY]} "
780 L"{[embedded_audio:EMBEDDED_AUDIO]} "
781 L"{[key_only:KEY_ONLY]} "
782 L"{CHANNEL_LAYOUT [channel_layout:string]}");
783 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
785 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
787 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
788 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
789 ->item(L"low_latency", L"Tries to enable low latency if given.")
790 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
792 L" will extract only the alpha channel from the "
793 L"channel. This is useful when you have two SDI video cards, and neither has native support "
794 L"for separate fill/key output")
795 ->item(L"channel_layout", L"If specified, overrides the audio channel layout used by the channel.");
796 sink.para()->text(L"Examples:");
797 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
798 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
799 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
801 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
802 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.");
804 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
805 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
808 spl::shared_ptr<core::frame_consumer> create_consumer(
809 const std::vector<std::wstring>& params, core::interaction_sink*)
811 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
812 return core::frame_consumer::empty();
814 configuration config;
816 if (params.size() > 1)
817 config.device_index = boost::lexical_cast<int>(params.at(1));
819 if (contains_param(L"INTERNAL_KEY", params))
820 config.keyer = configuration::keyer_t::internal_keyer;
821 else if (contains_param(L"EXTERNAL_KEY", params))
822 config.keyer = configuration::keyer_t::external_keyer;
823 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
824 config.keyer = configuration::keyer_t::external_separate_device_keyer;
826 config.keyer = configuration::keyer_t::default_keyer;
828 if (contains_param(L"LOW_LATENCY", params))
829 config.latency = configuration::latency_t::low_latency;
831 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
832 config.key_only = contains_param(L"KEY_ONLY", params);
834 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params);
836 if (!channel_layout.empty())
838 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
841 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout + L" not found."));
843 config.out_channel_layout = *found_layout;
846 bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
848 if (old_configuration_api)
849 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
851 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
854 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
855 const boost::property_tree::wptree& ptree, core::interaction_sink*)
857 configuration config;
859 auto keyer = ptree.get(L"keyer", L"default");
860 if(keyer == L"external")
861 config.keyer = configuration::keyer_t::external_keyer;
862 else if(keyer == L"internal")
863 config.keyer = configuration::keyer_t::internal_keyer;
864 else if (keyer == L"external_separate_device")
865 config.keyer = configuration::keyer_t::external_separate_device_keyer;
867 auto latency = ptree.get(L"latency", L"default");
868 if(latency == L"low")
869 config.latency = configuration::latency_t::low_latency;
870 else if(latency == L"normal")
871 config.latency = configuration::latency_t::normal_latency;
873 auto channel_layout = ptree.get_optional<std::wstring>(L"channel-layout");
877 CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
879 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
882 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
884 config.out_channel_layout = *found_layout;
887 config.key_only = ptree.get(L"key-only", config.key_only);
888 config.device_index = ptree.get(L"device", config.device_index);
889 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
890 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
891 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
893 bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
895 if (old_configuration_api)
896 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
898 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
904 ##############################################################################
910 BMD Developer Support
911 developer@blackmagic-design.com
913 -----------------------------------------------------------------------------
915 Thanks for your inquiry. The minimum number of frames that you can preroll
916 for scheduled playback is three frames for video and four frames for audio.
917 As you mentioned if you preroll less frames then playback will not start or
918 playback will be very sporadic. From our experience with Media Express, we
919 recommended that at least seven frames are prerolled for smooth playback.
921 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
922 There can be around 3 frames worth of latency on scheduled output.
923 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
924 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
925 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
926 guarantee that the provided frame will be output as soon the previous
927 frame output has been completed.
928 ################################################################################
932 ##############################################################################
933 Async DMA Transfer without redundant copying
938 BMD Developer Support
939 developer@blackmagic-design.com
941 -----------------------------------------------------------------------------
943 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
944 and providing a pointer to your video buffer when GetBytes() is called.
945 This may help to keep copying to a minimum. Please ensure that the pixel
946 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
947 have to colourspace convert which may result in additional copying.
948 ################################################################################