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>
60 namespace caspar { namespace decklink {
68 external_separate_device_keyer,
69 default_keyer = external_keyer
76 default_latency = normal_latency
80 int key_device_idx = 0;
81 bool embedded_audio = true;
82 keyer_t keyer = keyer_t::default_keyer;
83 latency_t latency = latency_t::default_latency;
84 bool key_only = false;
85 int base_buffer_depth = 3;
86 core::audio_channel_layout out_channel_layout = core::audio_channel_layout::invalid();
88 int buffer_depth() const
90 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1);
93 int key_device_index() const
95 return key_device_idx == 0 ? device_index + 1 : key_device_idx;
98 core::audio_channel_layout get_adjusted_layout(const core::audio_channel_layout& in_layout) const
100 auto adjusted = out_channel_layout == core::audio_channel_layout::invalid() ? in_layout : out_channel_layout;
102 if (adjusted.num_channels == 1) // Duplicate mono-signal into both left and right.
104 adjusted.num_channels = 2;
105 adjusted.channel_order.push_back(adjusted.channel_order.at(0)); // Usually FC -> FC FC
107 else if (adjusted.num_channels == 2)
109 adjusted.num_channels = 2;
111 else if (adjusted.num_channels <= 8)
113 adjusted.num_channels = 8;
115 else // Over 8 always pad to 16 or drop >16
117 adjusted.num_channels = 16;
124 template <typename Configuration>
126 const com_iface_ptr<Configuration>& config,
127 configuration::latency_t latency,
128 const std::wstring& print)
130 if (latency == configuration::latency_t::low_latency)
132 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
133 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";
135 else if (latency == configuration::latency_t::normal_latency)
137 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
138 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";
143 const com_iface_ptr<IDeckLinkAttributes>& attributes,
144 const com_iface_ptr<IDeckLinkKeyer>& decklink_keyer,
145 configuration::keyer_t keyer,
146 const std::wstring& print)
148 if (keyer == configuration::keyer_t::internal_keyer)
151 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
152 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
153 else if (FAILED(decklink_keyer->Enable(FALSE)))
154 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
155 else if (FAILED(decklink_keyer->SetLevel(255)))
156 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
158 CASPAR_LOG(info) << print << L" Enabled internal keyer.";
160 else if (keyer == configuration::keyer_t::external_keyer)
163 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
164 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
165 else if (FAILED(decklink_keyer->Enable(TRUE)))
166 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
167 else if (FAILED(decklink_keyer->SetLevel(255)))
168 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
170 CASPAR_LOG(info) << print << L" Enabled external keyer.";
174 class decklink_frame : public IDeckLinkVideoFrame
176 tbb::atomic<int> ref_count_;
177 core::const_frame frame_;
178 const core::video_format_desc format_desc_;
180 const bool key_only_;
182 cache_aligned_vector<no_init_proxy<uint8_t>> data_;
184 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only, bool will_attempt_dma)
186 , format_desc_(format_desc)
187 , key_only_(key_only)
191 bool dma_transfer_from_gl_buffer_impossible;
193 #if !defined(_MSC_VER)
194 // On Linux Decklink cannot DMA transfer from memory returned by glMapBuffer (at least on nvidia)
195 dma_transfer_from_gl_buffer_impossible = true;
197 // On Windows it is possible.
198 dma_transfer_from_gl_buffer_impossible = false;
201 needs_to_copy_ = will_attempt_dma && dma_transfer_from_gl_buffer_impossible;
206 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
208 return E_NOINTERFACE;
211 virtual ULONG STDMETHODCALLTYPE AddRef()
216 virtual ULONG STDMETHODCALLTYPE Release()
218 if(--ref_count_ == 0)
223 // IDecklinkVideoFrame
225 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
226 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
227 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
228 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
229 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
231 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
235 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
237 data_.resize(format_desc_.size);
238 *buffer = data_.data();
244 data_.resize(frame_.image_data().size());
245 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
247 *buffer = data_.data();
251 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
255 data_.resize(frame_.image_data().size());
256 fast_memcpy(data_.data(), *buffer, frame_.image_data().size());
257 *buffer = data_.data();
263 CASPAR_LOG_CURRENT_EXCEPTION();
270 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
271 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
275 const core::audio_buffer& audio_data()
277 return frame_.audio_data();
280 int64_t get_age_millis() const
282 return frame_.get_age_millis();
286 template <typename Configuration>
287 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
289 const configuration config_;
290 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
291 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
292 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
293 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
294 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
295 tbb::atomic<int64_t> current_presentation_delay_;
296 tbb::atomic<int64_t> scheduled_frames_completed_;
298 key_video_context(const configuration& config, const std::wstring& print)
301 current_presentation_delay_ = 0;
302 scheduled_frames_completed_ = 0;
304 set_latency(configuration_, config.latency, print);
305 set_keyer(attributes_, keyer_, config.keyer, print);
307 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
308 CASPAR_THROW_EXCEPTION(caspar_exception()
309 << msg_info(print + L" Failed to set key playback completion callback.")
310 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
313 template<typename Print>
314 void enable_video(BMDDisplayMode display_mode, const Print& print)
316 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
317 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable key video output."));
319 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
320 CASPAR_THROW_EXCEPTION(caspar_exception()
321 << msg_info(print() + L" Failed to set key playback completion callback.")
322 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
325 virtual ~key_video_context()
329 output_->StopScheduledPlayback(0, nullptr, 0);
330 output_->DisableVideoOutput();
334 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
335 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
336 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
338 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
343 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
344 IDeckLinkVideoFrame* completed_frame,
345 BMDOutputFrameCompletionResult result)
347 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
348 current_presentation_delay_ = dframe->get_age_millis();
349 ++scheduled_frames_completed_;
351 // Let the fill callback keep the pace, so no scheduling here.
357 template <typename Configuration>
358 struct decklink_consumer : public IDeckLinkVideoOutputCallback, boost::noncopyable
360 const int channel_index_;
361 const configuration config_;
363 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
364 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
365 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
366 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
367 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
369 tbb::spin_mutex exception_mutex_;
370 std::exception_ptr exception_;
372 tbb::atomic<bool> is_running_;
374 const std::wstring model_name_ = get_model_name(decklink_);
375 bool will_attempt_dma_;
376 const core::video_format_desc format_desc_;
377 const core::audio_channel_layout in_channel_layout_;
378 const core::audio_channel_layout out_channel_layout_ = config_.get_adjusted_layout(in_channel_layout_);
379 core::audio_channel_remapper channel_remapper_ { in_channel_layout_, out_channel_layout_ };
380 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
382 long long video_scheduled_ = 0;
383 long long audio_scheduled_ = 0;
385 int preroll_count_ = 0;
387 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
389 tbb::concurrent_bounded_queue<core::const_frame> frame_buffer_;
391 spl::shared_ptr<diagnostics::graph> graph_;
392 caspar::timer tick_timer_;
393 retry_task<bool> send_completion_;
394 reference_signal_detector reference_signal_detector_ { output_ };
395 tbb::atomic<int64_t> current_presentation_delay_;
396 tbb::atomic<int64_t> scheduled_frames_completed_;
397 std::unique_ptr<key_video_context<Configuration>> key_context_;
401 const configuration& config,
402 const core::video_format_desc& format_desc,
403 const core::audio_channel_layout& in_channel_layout,
405 : channel_index_(channel_index)
407 , format_desc_(format_desc)
408 , in_channel_layout_(in_channel_layout)
411 current_presentation_delay_ = 0;
412 scheduled_frames_completed_ = 0;
414 frame_buffer_.set_capacity(1);
416 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
417 key_context_.reset(new key_video_context<Configuration>(config, print()));
419 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
420 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
421 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
422 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
423 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
424 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
428 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
431 graph_->set_text(print());
432 diagnostics::register_graph(graph_);
434 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, will_attempt_dma_));
436 if(config.embedded_audio)
439 set_latency(configuration_, config.latency, print());
440 set_keyer(attributes_, keyer_, config.keyer, print());
442 if(config.embedded_audio)
443 output_->BeginAudioPreroll();
445 for (int n = 0; n < buffer_size_; ++n)
447 if (config.embedded_audio)
448 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[n % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
450 schedule_next_video(core::const_frame::empty());
453 if (config.embedded_audio)
455 // Preroll one extra frame worth of audio
456 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[buffer_size_ % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
457 output_->EndAudioPreroll();
466 frame_buffer_.try_push(core::const_frame::empty());
468 if(output_ != nullptr)
470 output_->StopScheduledPlayback(0, nullptr, 0);
471 if(config_.embedded_audio)
472 output_->DisableAudioOutput();
473 output_->DisableVideoOutput();
479 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
480 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable audio output."));
482 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
485 void enable_video(BMDDisplayMode display_mode)
487 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
488 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable fill video output."));
490 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
491 CASPAR_THROW_EXCEPTION(caspar_exception()
492 << msg_info(print() + L" Failed to set fill playback completion callback.")
493 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
496 key_context_->enable_video(display_mode, [this]() { return print(); });
499 void start_playback()
501 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
502 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule fill playback."));
504 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
505 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule key playback."));
508 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
509 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
510 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
512 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
515 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
519 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
526 auto tick_time = tick_timer_.elapsed()*format_desc_.fps * 0.5;
527 graph_->set_value("tick-time", tick_time);
528 tick_timer_.restart();
530 reference_signal_detector_.detect_change([this]() { return print(); });
532 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
533 current_presentation_delay_ = dframe->get_age_millis();
534 ++scheduled_frames_completed_;
540 scheduled_frames_completed_
541 - key_context_->scheduled_frames_completed_)
544 if(result == bmdOutputFrameDisplayedLate)
546 graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
547 video_scheduled_ += format_desc_.duration;
548 audio_scheduled_ += dframe->audio_data().size() / in_channel_layout_.num_channels;
550 else if(result == bmdOutputFrameDropped)
551 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
552 else if(result == bmdOutputFrameFlushed)
553 graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
556 output_->GetBufferedVideoFrameCount(&buffered);
557 graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
559 if (config_.embedded_audio)
561 output_->GetBufferedAudioSampleFrameCount(&buffered);
562 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
565 auto frame = core::const_frame::empty();
567 frame_buffer_.pop(frame);
569 send_completion_.try_completion();
571 if (config_.embedded_audio)
572 schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
574 schedule_next_video(frame);
578 lock(exception_mutex_, [&]
580 exception_ = std::current_exception();
589 void schedule_next_audio(const T& audio_data)
591 auto sample_frame_count = static_cast<int>(audio_data.size()/out_channel_layout_.num_channels);
593 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
595 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
596 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
598 audio_scheduled_ += sample_frame_count;
601 void schedule_next_video(core::const_frame frame)
605 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true, will_attempt_dma_));
606 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
607 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
610 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only, will_attempt_dma_));
611 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
612 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
614 video_scheduled_ += format_desc_.duration;
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(print() + L" Is not running."));
632 auto enqueue_task = [ready, frame, this]() mutable -> boost::optional<bool>
635 ready = frame_buffer_.try_push(frame);
640 return boost::optional<bool>();
643 send_completion_.set_task(enqueue_task);
644 send_completion_.try_completion();
646 return send_completion_.get_future();
649 std::wstring print() const
651 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
652 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
653 boost::lexical_cast<std::wstring>(config_.device_index) +
655 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
657 format_desc_.name + L"]";
659 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
660 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
664 template <typename Configuration>
665 struct decklink_consumer_proxy : public core::frame_consumer
667 core::monitor::subject monitor_subject_;
668 const configuration config_;
669 std::unique_ptr<decklink_consumer<Configuration>> consumer_;
670 core::video_format_desc format_desc_;
674 decklink_consumer_proxy(const configuration& config)
676 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
678 auto ctx = core::diagnostics::call_context::for_thread();
679 executor_.begin_invoke([=]
681 core::diagnostics::call_context::for_thread() = ctx;
686 ~decklink_consumer_proxy()
697 void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
699 format_desc_ = format_desc;
703 consumer_.reset(new decklink_consumer<Configuration>(config_, format_desc, channel_layout, channel_index));
707 std::future<bool> send(core::const_frame frame) override
709 return consumer_->send(frame);
712 std::wstring print() const override
714 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
717 std::wstring name() const override
722 boost::property_tree::wptree info() const override
724 boost::property_tree::wptree info;
725 info.add(L"type", L"decklink");
726 info.add(L"key-only", config_.key_only);
727 info.add(L"device", config_.device_index);
729 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
731 info.add(L"key-device", config_.key_device_index());
734 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
735 info.add(L"embedded-audio", config_.embedded_audio);
736 info.add(L"presentation-frame-age", presentation_frame_age_millis());
737 //info.add(L"internal-key", config_.internal_key);
741 int buffer_depth() const override
743 return config_.buffer_depth() + 2;
746 int index() const override
748 return 300 + config_.device_index;
751 int64_t presentation_frame_age_millis() const override
753 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
756 core::monitor::subject& monitor_output()
758 return monitor_subject_;
762 const software_version<3>& get_driver_version()
764 static software_version<3> version(u8(get_version()));
769 const software_version<3> get_new_configuration_api_version()
771 static software_version<3> NEW_CONFIGURATION_API("10.2");
773 return NEW_CONFIGURATION_API;
776 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
778 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
779 sink.syntax(L"DECKLINK "
780 L"{[device_index:int]|1} "
781 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
782 L"{[low_latency:LOW_LATENCY]} "
783 L"{[embedded_audio:EMBEDDED_AUDIO]} "
784 L"{[key_only:KEY_ONLY]} "
785 L"{CHANNEL_LAYOUT [channel_layout:string]}");
786 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
788 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
790 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
791 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
792 ->item(L"low_latency", L"Tries to enable low latency if given.")
793 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
795 L" will extract only the alpha channel from the "
796 L"channel. This is useful when you have two SDI video cards, and neither has native support "
797 L"for separate fill/key output")
798 ->item(L"channel_layout", L"If specified, overrides the audio channel layout used by the channel.");
799 sink.para()->text(L"Examples:");
800 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
801 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
802 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
804 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
805 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.");
807 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
808 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
811 spl::shared_ptr<core::frame_consumer> create_consumer(
812 const std::vector<std::wstring>& params, core::interaction_sink*)
814 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
815 return core::frame_consumer::empty();
817 configuration config;
819 if (params.size() > 1)
820 config.device_index = boost::lexical_cast<int>(params.at(1));
822 if (contains_param(L"INTERNAL_KEY", params))
823 config.keyer = configuration::keyer_t::internal_keyer;
824 else if (contains_param(L"EXTERNAL_KEY", params))
825 config.keyer = configuration::keyer_t::external_keyer;
826 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
827 config.keyer = configuration::keyer_t::external_separate_device_keyer;
829 config.keyer = configuration::keyer_t::default_keyer;
831 if (contains_param(L"LOW_LATENCY", params))
832 config.latency = configuration::latency_t::low_latency;
834 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
835 config.key_only = contains_param(L"KEY_ONLY", params);
837 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params);
839 if (!channel_layout.empty())
841 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
844 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout + L" not found."));
846 config.out_channel_layout = *found_layout;
849 bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
851 if (old_configuration_api)
852 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
854 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
857 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
858 const boost::property_tree::wptree& ptree, core::interaction_sink*)
860 configuration config;
862 auto keyer = ptree.get(L"keyer", L"default");
863 if(keyer == L"external")
864 config.keyer = configuration::keyer_t::external_keyer;
865 else if(keyer == L"internal")
866 config.keyer = configuration::keyer_t::internal_keyer;
867 else if (keyer == L"external_separate_device")
868 config.keyer = configuration::keyer_t::external_separate_device_keyer;
870 auto latency = ptree.get(L"latency", L"default");
871 if(latency == L"low")
872 config.latency = configuration::latency_t::low_latency;
873 else if(latency == L"normal")
874 config.latency = configuration::latency_t::normal_latency;
876 auto channel_layout = ptree.get_optional<std::wstring>(L"channel-layout");
880 CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
882 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
885 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
887 config.out_channel_layout = *found_layout;
890 config.key_only = ptree.get(L"key-only", config.key_only);
891 config.device_index = ptree.get(L"device", config.device_index);
892 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
893 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
894 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
896 bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
898 if (old_configuration_api)
899 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
901 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
907 ##############################################################################
913 BMD Developer Support
914 developer@blackmagic-design.com
916 -----------------------------------------------------------------------------
918 Thanks for your inquiry. The minimum number of frames that you can preroll
919 for scheduled playback is three frames for video and four frames for audio.
920 As you mentioned if you preroll less frames then playback will not start or
921 playback will be very sporadic. From our experience with Media Express, we
922 recommended that at least seven frames are prerolled for smooth playback.
924 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
925 There can be around 3 frames worth of latency on scheduled output.
926 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
927 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
928 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
929 guarantee that the provided frame will be output as soon the previous
930 frame output has been completed.
931 ################################################################################
935 ##############################################################################
936 Async DMA Transfer without redundant copying
941 BMD Developer Support
942 developer@blackmagic-design.com
944 -----------------------------------------------------------------------------
946 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
947 and providing a pointer to your video buffer when GetBytes() is called.
948 This may help to keep copying to a minimum. Please ensure that the pixel
949 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
950 have to colourspace convert which may result in additional copying.
951 ################################################################################