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/no_init_proxy.h>
45 #include <common/array.h>
46 #include <common/future.h>
47 #include <common/cache_aligned_vector.h>
48 #include <common/timer.h>
49 #include <common/param.h>
50 #include <common/software_version.h>
52 #include <tbb/concurrent_queue.h>
54 #include <common/assert.h>
55 #include <boost/lexical_cast.hpp>
56 #include <boost/circular_buffer.hpp>
57 #include <boost/property_tree/ptree.hpp>
58 #include <boost/thread/mutex.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 = false;
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)
230 // IDecklinkVideoFrame
232 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
233 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
234 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
235 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
236 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
238 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
242 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
244 data_.resize(format_desc_.size);
245 *buffer = data_.data();
251 data_.resize(frame_.image_data().size());
252 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
254 *buffer = data_.data();
258 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
262 data_.resize(frame_.image_data().size());
263 std::memcpy(data_.data(), *buffer, frame_.image_data().size());
264 *buffer = data_.data();
270 CASPAR_LOG_CURRENT_EXCEPTION();
277 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
278 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
282 const core::audio_buffer& audio_data()
284 return frame_.audio_data();
287 int64_t get_age_millis() const
289 return frame_.get_age_millis();
293 template <typename Configuration>
294 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
296 const configuration config_;
297 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
298 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
299 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
300 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
301 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
302 tbb::atomic<int64_t> current_presentation_delay_;
303 tbb::atomic<int64_t> scheduled_frames_completed_;
305 key_video_context(const configuration& config, const std::wstring& print)
308 current_presentation_delay_ = 0;
309 scheduled_frames_completed_ = 0;
311 set_latency(configuration_, config.latency, print);
312 set_keyer(attributes_, keyer_, config.keyer, print);
314 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
315 CASPAR_THROW_EXCEPTION(caspar_exception()
316 << msg_info(print + L" Failed to set key playback completion callback.")
317 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
320 template<typename Print>
321 void enable_video(BMDDisplayMode display_mode, const Print& print)
323 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
324 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable key video output."));
326 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
327 CASPAR_THROW_EXCEPTION(caspar_exception()
328 << msg_info(print() + L" Failed to set key playback completion callback.")
329 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
332 virtual ~key_video_context()
336 output_->StopScheduledPlayback(0, nullptr, 0);
337 output_->DisableVideoOutput();
341 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
342 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
343 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
345 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
350 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
351 IDeckLinkVideoFrame* completed_frame,
352 BMDOutputFrameCompletionResult result)
354 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
355 current_presentation_delay_ = dframe->get_age_millis();
356 ++scheduled_frames_completed_;
358 // Let the fill callback keep the pace, so no scheduling here.
364 template <typename Configuration>
365 struct decklink_consumer : public IDeckLinkVideoOutputCallback, boost::noncopyable
367 const int channel_index_;
368 const configuration config_;
370 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
371 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
372 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
373 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
374 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
376 tbb::spin_mutex exception_mutex_;
377 std::exception_ptr exception_;
379 tbb::atomic<bool> is_running_;
381 const std::wstring model_name_ = get_model_name(decklink_);
382 bool will_attempt_dma_;
383 const core::video_format_desc format_desc_;
384 const core::audio_channel_layout in_channel_layout_;
385 const core::audio_channel_layout out_channel_layout_ = config_.get_adjusted_layout(in_channel_layout_);
386 core::audio_channel_remapper channel_remapper_ { in_channel_layout_, out_channel_layout_ };
387 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
389 long long video_scheduled_ = 0;
390 long long audio_scheduled_ = 0;
392 int preroll_count_ = 0;
394 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
396 tbb::concurrent_bounded_queue<core::const_frame> frame_buffer_;
397 caspar::semaphore ready_for_new_frames_ { 0 };
399 spl::shared_ptr<diagnostics::graph> graph_;
400 caspar::timer tick_timer_;
401 reference_signal_detector reference_signal_detector_ { output_ };
402 tbb::atomic<int64_t> current_presentation_delay_;
403 tbb::atomic<int64_t> scheduled_frames_completed_;
404 std::unique_ptr<key_video_context<Configuration>> key_context_;
408 const configuration& config,
409 const core::video_format_desc& format_desc,
410 const core::audio_channel_layout& in_channel_layout,
412 : channel_index_(channel_index)
414 , format_desc_(format_desc)
415 , in_channel_layout_(in_channel_layout)
418 current_presentation_delay_ = 0;
419 scheduled_frames_completed_ = 0;
421 frame_buffer_.set_capacity(1);
423 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
424 key_context_.reset(new key_video_context<Configuration>(config, print()));
426 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
427 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
428 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
429 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
430 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
431 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
435 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
438 graph_->set_text(print());
439 diagnostics::register_graph(graph_);
441 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, will_attempt_dma_));
443 if(config.embedded_audio)
446 set_latency(configuration_, config.latency, print());
447 set_keyer(attributes_, keyer_, config.keyer, print());
449 if(config.embedded_audio)
450 output_->BeginAudioPreroll();
452 for (int n = 0; n < buffer_size_; ++n)
454 if (config.embedded_audio)
455 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[n % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
457 schedule_next_video(core::const_frame::empty());
460 if (config.embedded_audio)
462 // Preroll one extra frame worth of audio
463 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[buffer_size_ % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
464 output_->EndAudioPreroll();
473 frame_buffer_.try_push(core::const_frame::empty());
475 if(output_ != nullptr)
477 output_->StopScheduledPlayback(0, nullptr, 0);
478 if(config_.embedded_audio)
479 output_->DisableAudioOutput();
480 output_->DisableVideoOutput();
486 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
487 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable audio output."));
489 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
492 void enable_video(BMDDisplayMode display_mode)
494 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
495 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable fill video output."));
497 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
498 CASPAR_THROW_EXCEPTION(caspar_exception()
499 << msg_info(print() + L" Failed to set fill playback completion callback.")
500 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
503 key_context_->enable_video(display_mode, [this]() { return print(); });
506 void start_playback()
508 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
509 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule fill playback."));
511 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
512 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule key playback."));
515 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
516 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
517 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
519 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
522 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
526 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
533 auto tick_time = tick_timer_.elapsed()*format_desc_.fps * 0.5;
534 graph_->set_value("tick-time", tick_time);
535 tick_timer_.restart();
537 reference_signal_detector_.detect_change([this]() { return print(); });
539 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
540 current_presentation_delay_ = dframe->get_age_millis();
541 ++scheduled_frames_completed_;
547 scheduled_frames_completed_
548 - key_context_->scheduled_frames_completed_)
551 if(result == bmdOutputFrameDisplayedLate)
553 graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
554 video_scheduled_ += format_desc_.duration;
555 audio_scheduled_ += dframe->audio_data().size() / in_channel_layout_.num_channels;
557 else if(result == bmdOutputFrameDropped)
558 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
559 else if(result == bmdOutputFrameFlushed)
560 graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
563 output_->GetBufferedVideoFrameCount(&buffered);
564 graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
566 if (config_.embedded_audio)
568 output_->GetBufferedAudioSampleFrameCount(&buffered);
569 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
572 auto frame = core::const_frame::empty();
574 frame_buffer_.pop(frame);
575 ready_for_new_frames_.release();
580 if (config_.embedded_audio)
581 schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
583 schedule_next_video(frame);
587 lock(exception_mutex_, [&]
589 exception_ = std::current_exception();
598 void schedule_next_audio(const T& audio_data)
600 auto sample_frame_count = static_cast<int>(audio_data.size()/out_channel_layout_.num_channels);
602 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
604 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
605 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
607 audio_scheduled_ += sample_frame_count;
610 void schedule_next_video(core::const_frame frame)
614 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true, will_attempt_dma_));
615 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
616 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
619 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only, will_attempt_dma_));
620 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
621 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
623 video_scheduled_ += format_desc_.duration;
626 std::future<bool> send(core::const_frame frame)
628 auto exception = lock(exception_mutex_, [&]
633 if(exception != nullptr)
634 std::rethrow_exception(exception);
637 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Is not running."));
639 frame_buffer_.push(frame);
641 auto send_completion = spl::make_shared<std::promise<bool>>();
643 ready_for_new_frames_.acquire(1, [send_completion]
645 send_completion->set_value(true);
648 return send_completion->get_future();
651 std::wstring print() const
653 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
654 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
655 boost::lexical_cast<std::wstring>(config_.device_index) +
657 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
659 format_desc_.name + L"]";
661 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
662 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
666 template <typename Configuration>
667 struct decklink_consumer_proxy : public core::frame_consumer
669 core::monitor::subject monitor_subject_;
670 const configuration config_;
671 std::unique_ptr<decklink_consumer<Configuration>> consumer_;
672 core::video_format_desc format_desc_;
676 decklink_consumer_proxy(const configuration& config)
678 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
680 auto ctx = core::diagnostics::call_context::for_thread();
681 executor_.begin_invoke([=]
683 core::diagnostics::call_context::for_thread() = ctx;
688 ~decklink_consumer_proxy()
699 void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
701 format_desc_ = format_desc;
705 consumer_.reset(new decklink_consumer<Configuration>(config_, format_desc, channel_layout, channel_index));
709 std::future<bool> send(core::const_frame frame) override
711 return consumer_->send(frame);
714 std::wstring print() const override
716 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
719 std::wstring name() const override
724 boost::property_tree::wptree info() const override
726 boost::property_tree::wptree info;
727 info.add(L"type", L"decklink");
728 info.add(L"key-only", config_.key_only);
729 info.add(L"device", config_.device_index);
731 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
733 info.add(L"key-device", config_.key_device_index());
736 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
737 info.add(L"embedded-audio", config_.embedded_audio);
738 info.add(L"presentation-frame-age", presentation_frame_age_millis());
739 //info.add(L"internal-key", config_.internal_key);
743 int buffer_depth() const override
745 return config_.buffer_depth() + 2;
748 int index() const override
750 return 300 + config_.device_index;
753 int64_t presentation_frame_age_millis() const override
755 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
758 core::monitor::subject& monitor_output()
760 return monitor_subject_;
764 const software_version<3>& get_driver_version()
766 static software_version<3> version(u8(get_version()));
771 const software_version<3> get_new_configuration_api_version()
773 static software_version<3> NEW_CONFIGURATION_API("10.2");
775 return NEW_CONFIGURATION_API;
778 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
780 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
781 sink.syntax(L"DECKLINK "
782 L"{[device_index:int]|1} "
783 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
784 L"{[low_latency:LOW_LATENCY]} "
785 L"{[embedded_audio:EMBEDDED_AUDIO]} "
786 L"{[key_only:KEY_ONLY]} "
787 L"{CHANNEL_LAYOUT [channel_layout:string]}");
788 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
790 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
792 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
793 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
794 ->item(L"low_latency", L"Tries to enable low latency if given.")
795 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
797 L" will extract only the alpha channel from the "
798 L"channel. This is useful when you have two SDI video cards, and neither has native support "
799 L"for separate fill/key output")
800 ->item(L"channel_layout", L"If specified, overrides the audio channel layout used by the channel.");
801 sink.para()->text(L"Examples:");
802 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
803 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
804 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
806 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
807 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.");
809 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
810 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
813 spl::shared_ptr<core::frame_consumer> create_consumer(
814 const std::vector<std::wstring>& params, core::interaction_sink*, std::vector<spl::shared_ptr<core::video_channel>> channels)
816 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
817 return core::frame_consumer::empty();
819 configuration config;
821 if (params.size() > 1)
822 config.device_index = boost::lexical_cast<int>(params.at(1));
824 if (contains_param(L"INTERNAL_KEY", params))
825 config.keyer = configuration::keyer_t::internal_keyer;
826 else if (contains_param(L"EXTERNAL_KEY", params))
827 config.keyer = configuration::keyer_t::external_keyer;
828 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
829 config.keyer = configuration::keyer_t::external_separate_device_keyer;
831 config.keyer = configuration::keyer_t::default_keyer;
833 if (contains_param(L"LOW_LATENCY", params))
834 config.latency = configuration::latency_t::low_latency;
836 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
837 config.key_only = contains_param(L"KEY_ONLY", params);
839 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params);
841 if (!channel_layout.empty())
843 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
846 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout + L" not found."));
848 config.out_channel_layout = *found_layout;
851 bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
853 if (old_configuration_api)
854 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
856 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
859 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
860 const boost::property_tree::wptree& ptree, core::interaction_sink*, std::vector<spl::shared_ptr<core::video_channel>> channels)
862 configuration config;
864 auto keyer = ptree.get(L"keyer", L"default");
865 if(keyer == L"external")
866 config.keyer = configuration::keyer_t::external_keyer;
867 else if(keyer == L"internal")
868 config.keyer = configuration::keyer_t::internal_keyer;
869 else if (keyer == L"external_separate_device")
870 config.keyer = configuration::keyer_t::external_separate_device_keyer;
872 auto latency = ptree.get(L"latency", L"default");
873 if(latency == L"low")
874 config.latency = configuration::latency_t::low_latency;
875 else if(latency == L"normal")
876 config.latency = configuration::latency_t::normal_latency;
878 auto channel_layout = ptree.get_optional<std::wstring>(L"channel-layout");
882 CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
884 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
887 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
889 config.out_channel_layout = *found_layout;
892 config.key_only = ptree.get(L"key-only", config.key_only);
893 config.device_index = ptree.get(L"device", config.device_index);
894 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
895 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
896 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
898 bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
900 if (old_configuration_api)
901 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
903 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
909 ##############################################################################
915 BMD Developer Support
916 developer@blackmagic-design.com
918 -----------------------------------------------------------------------------
920 Thanks for your inquiry. The minimum number of frames that you can preroll
921 for scheduled playback is three frames for video and four frames for audio.
922 As you mentioned if you preroll less frames then playback will not start or
923 playback will be very sporadic. From our experience with Media Express, we
924 recommended that at least seven frames are prerolled for smooth playback.
926 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
927 There can be around 3 frames worth of latency on scheduled output.
928 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
929 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
930 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
931 guarantee that the provided frame will be output as soon the previous
932 frame output has been completed.
933 ################################################################################
937 ##############################################################################
938 Async DMA Transfer without redundant copying
943 BMD Developer Support
944 developer@blackmagic-design.com
946 -----------------------------------------------------------------------------
948 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
949 and providing a pointer to your video buffer when GetBytes() is called.
950 This may help to keep copying to a minimum. Please ensure that the pixel
951 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
952 have to colourspace convert which may result in additional copying.
953 ################################################################################