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>
59 #include <boost/thread/mutex.hpp>
63 namespace caspar { namespace decklink {
71 external_separate_device_keyer,
72 default_keyer = external_keyer
79 default_latency = normal_latency
83 int key_device_idx = 0;
84 bool embedded_audio = true;
85 keyer_t keyer = keyer_t::default_keyer;
86 latency_t latency = latency_t::default_latency;
87 bool key_only = false;
88 int base_buffer_depth = 3;
89 core::audio_channel_layout out_channel_layout = core::audio_channel_layout::invalid();
91 int buffer_depth() const
93 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1);
96 int key_device_index() const
98 return key_device_idx == 0 ? device_index + 1 : key_device_idx;
101 core::audio_channel_layout get_adjusted_layout(const core::audio_channel_layout& in_layout) const
103 auto adjusted = out_channel_layout == core::audio_channel_layout::invalid() ? in_layout : out_channel_layout;
105 if (adjusted.num_channels == 1) // Duplicate mono-signal into both left and right.
107 adjusted.num_channels = 2;
108 adjusted.channel_order.push_back(adjusted.channel_order.at(0)); // Usually FC -> FC FC
110 else if (adjusted.num_channels == 2)
112 adjusted.num_channels = 2;
114 else if (adjusted.num_channels <= 8)
116 adjusted.num_channels = 8;
118 else // Over 8 always pad to 16 or drop >16
120 adjusted.num_channels = 16;
127 template <typename Configuration>
129 const com_iface_ptr<Configuration>& config,
130 configuration::latency_t latency,
131 const std::wstring& print)
133 if (latency == configuration::latency_t::low_latency)
135 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
136 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";
138 else if (latency == configuration::latency_t::normal_latency)
140 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
141 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";
146 const com_iface_ptr<IDeckLinkAttributes>& attributes,
147 const com_iface_ptr<IDeckLinkKeyer>& decklink_keyer,
148 configuration::keyer_t keyer,
149 const std::wstring& print)
151 if (keyer == configuration::keyer_t::internal_keyer)
154 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
155 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
156 else if (FAILED(decklink_keyer->Enable(FALSE)))
157 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
158 else if (FAILED(decklink_keyer->SetLevel(255)))
159 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
161 CASPAR_LOG(info) << print << L" Enabled internal keyer.";
163 else if (keyer == configuration::keyer_t::external_keyer)
166 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
167 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
168 else if (FAILED(decklink_keyer->Enable(TRUE)))
169 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
170 else if (FAILED(decklink_keyer->SetLevel(255)))
171 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
173 CASPAR_LOG(info) << print << L" Enabled external keyer.";
177 class decklink_frame : public IDeckLinkVideoFrame
179 tbb::atomic<int> ref_count_;
180 core::const_frame frame_;
181 const core::video_format_desc format_desc_;
183 const bool key_only_;
185 cache_aligned_vector<no_init_proxy<uint8_t>> data_;
187 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only, bool will_attempt_dma)
189 , format_desc_(format_desc)
190 , key_only_(key_only)
194 bool dma_transfer_from_gl_buffer_impossible;
196 #if !defined(_MSC_VER)
197 // On Linux Decklink cannot DMA transfer from memory returned by glMapBuffer (at least on nvidia)
198 dma_transfer_from_gl_buffer_impossible = true;
200 // On Windows it is possible.
201 dma_transfer_from_gl_buffer_impossible = false;
204 needs_to_copy_ = will_attempt_dma && dma_transfer_from_gl_buffer_impossible;
209 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
211 return E_NOINTERFACE;
214 virtual ULONG STDMETHODCALLTYPE AddRef()
219 virtual ULONG STDMETHODCALLTYPE Release()
221 if(--ref_count_ == 0)
226 // IDecklinkVideoFrame
228 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
229 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
230 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
231 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
232 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
234 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
238 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
240 data_.resize(format_desc_.size);
241 *buffer = data_.data();
247 data_.resize(frame_.image_data().size());
248 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
250 *buffer = data_.data();
254 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
258 data_.resize(frame_.image_data().size());
259 fast_memcpy(data_.data(), *buffer, frame_.image_data().size());
260 *buffer = data_.data();
266 CASPAR_LOG_CURRENT_EXCEPTION();
273 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
274 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
278 const core::audio_buffer& audio_data()
280 return frame_.audio_data();
283 int64_t get_age_millis() const
285 return frame_.get_age_millis();
289 template <typename Configuration>
290 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
292 const configuration config_;
293 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
294 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
295 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
296 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
297 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
298 tbb::atomic<int64_t> current_presentation_delay_;
299 tbb::atomic<int64_t> scheduled_frames_completed_;
301 key_video_context(const configuration& config, const std::wstring& print)
304 current_presentation_delay_ = 0;
305 scheduled_frames_completed_ = 0;
307 set_latency(configuration_, config.latency, print);
308 set_keyer(attributes_, keyer_, config.keyer, print);
310 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
311 CASPAR_THROW_EXCEPTION(caspar_exception()
312 << msg_info(print + L" Failed to set key playback completion callback.")
313 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
316 template<typename Print>
317 void enable_video(BMDDisplayMode display_mode, const Print& print)
319 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
320 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable key video output."));
322 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
323 CASPAR_THROW_EXCEPTION(caspar_exception()
324 << msg_info(print() + L" Failed to set key playback completion callback.")
325 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
328 virtual ~key_video_context()
332 output_->StopScheduledPlayback(0, nullptr, 0);
333 output_->DisableVideoOutput();
337 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
338 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
339 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
341 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
346 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
347 IDeckLinkVideoFrame* completed_frame,
348 BMDOutputFrameCompletionResult result)
350 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
351 current_presentation_delay_ = dframe->get_age_millis();
352 ++scheduled_frames_completed_;
354 // Let the fill callback keep the pace, so no scheduling here.
360 template <typename Configuration>
361 struct decklink_consumer : public IDeckLinkVideoOutputCallback, boost::noncopyable
363 const int channel_index_;
364 const configuration config_;
366 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
367 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
368 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
369 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
370 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
372 tbb::spin_mutex exception_mutex_;
373 std::exception_ptr exception_;
375 tbb::atomic<bool> is_running_;
377 const std::wstring model_name_ = get_model_name(decklink_);
378 bool will_attempt_dma_;
379 const core::video_format_desc format_desc_;
380 const core::audio_channel_layout in_channel_layout_;
381 const core::audio_channel_layout out_channel_layout_ = config_.get_adjusted_layout(in_channel_layout_);
382 core::audio_channel_remapper channel_remapper_ { in_channel_layout_, out_channel_layout_ };
383 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
385 long long video_scheduled_ = 0;
386 long long audio_scheduled_ = 0;
388 int preroll_count_ = 0;
390 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
392 tbb::concurrent_bounded_queue<core::const_frame> frame_buffer_;
394 spl::shared_ptr<diagnostics::graph> graph_;
395 caspar::timer tick_timer_;
396 boost::mutex send_completion_mutex_;
397 std::packaged_task<bool ()> send_completion_;
398 reference_signal_detector reference_signal_detector_ { output_ };
399 tbb::atomic<int64_t> current_presentation_delay_;
400 tbb::atomic<int64_t> scheduled_frames_completed_;
401 std::unique_ptr<key_video_context<Configuration>> key_context_;
405 const configuration& config,
406 const core::video_format_desc& format_desc,
407 const core::audio_channel_layout& in_channel_layout,
409 : channel_index_(channel_index)
411 , format_desc_(format_desc)
412 , in_channel_layout_(in_channel_layout)
415 current_presentation_delay_ = 0;
416 scheduled_frames_completed_ = 0;
418 frame_buffer_.set_capacity(1);
420 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
421 key_context_.reset(new key_video_context<Configuration>(config, print()));
423 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
424 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
425 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
426 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
427 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
428 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
432 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
435 graph_->set_text(print());
436 diagnostics::register_graph(graph_);
438 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, will_attempt_dma_));
440 if(config.embedded_audio)
443 set_latency(configuration_, config.latency, print());
444 set_keyer(attributes_, keyer_, config.keyer, print());
446 if(config.embedded_audio)
447 output_->BeginAudioPreroll();
449 for (int n = 0; n < buffer_size_; ++n)
451 if (config.embedded_audio)
452 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[n % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
454 schedule_next_video(core::const_frame::empty());
457 if (config.embedded_audio)
459 // Preroll one extra frame worth of audio
460 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[buffer_size_ % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
461 output_->EndAudioPreroll();
470 frame_buffer_.try_push(core::const_frame::empty());
472 if(output_ != nullptr)
474 output_->StopScheduledPlayback(0, nullptr, 0);
475 if(config_.embedded_audio)
476 output_->DisableAudioOutput();
477 output_->DisableVideoOutput();
483 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
484 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable audio output."));
486 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
489 void enable_video(BMDDisplayMode display_mode)
491 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
492 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable fill video output."));
494 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
495 CASPAR_THROW_EXCEPTION(caspar_exception()
496 << msg_info(print() + L" Failed to set fill playback completion callback.")
497 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
500 key_context_->enable_video(display_mode, [this]() { return print(); });
503 void start_playback()
505 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
506 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule fill playback."));
508 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
509 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule key playback."));
512 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
513 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
514 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
516 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
519 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
523 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
530 auto tick_time = tick_timer_.elapsed()*format_desc_.fps * 0.5;
531 graph_->set_value("tick-time", tick_time);
532 tick_timer_.restart();
534 reference_signal_detector_.detect_change([this]() { return print(); });
536 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
537 current_presentation_delay_ = dframe->get_age_millis();
538 ++scheduled_frames_completed_;
544 scheduled_frames_completed_
545 - key_context_->scheduled_frames_completed_)
548 if(result == bmdOutputFrameDisplayedLate)
550 graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
551 video_scheduled_ += format_desc_.duration;
552 audio_scheduled_ += dframe->audio_data().size() / in_channel_layout_.num_channels;
554 else if(result == bmdOutputFrameDropped)
555 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
556 else if(result == bmdOutputFrameFlushed)
557 graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
560 output_->GetBufferedVideoFrameCount(&buffered);
561 graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
563 if (config_.embedded_audio)
565 output_->GetBufferedAudioSampleFrameCount(&buffered);
566 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
569 auto frame = core::const_frame::empty();
571 frame_buffer_.pop(frame);
574 boost::lock_guard<boost::mutex> lock(send_completion_mutex_);
576 if (send_completion_.valid())
579 send_completion_ = std::packaged_task<bool()>();
583 if (config_.embedded_audio)
584 schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
586 schedule_next_video(frame);
590 lock(exception_mutex_, [&]
592 exception_ = std::current_exception();
601 void schedule_next_audio(const T& audio_data)
603 auto sample_frame_count = static_cast<int>(audio_data.size()/out_channel_layout_.num_channels);
605 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
607 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
608 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
610 audio_scheduled_ += sample_frame_count;
613 void schedule_next_video(core::const_frame frame)
617 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true, will_attempt_dma_));
618 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
619 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
622 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only, will_attempt_dma_));
623 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
624 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
626 video_scheduled_ += format_desc_.duration;
629 std::future<bool> send(core::const_frame frame)
631 auto exception = lock(exception_mutex_, [&]
636 if(exception != nullptr)
637 std::rethrow_exception(exception);
640 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Is not running."));
642 if (frame_buffer_.try_push(frame))
643 return make_ready_future(true);
645 boost::lock_guard<boost::mutex> lock(send_completion_mutex_);
647 send_completion_ = std::packaged_task<bool ()>([frame, this] () mutable -> bool
649 frame_buffer_.push(frame);
654 return send_completion_.get_future();
657 std::wstring print() const
659 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
660 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
661 boost::lexical_cast<std::wstring>(config_.device_index) +
663 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
665 format_desc_.name + L"]";
667 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
668 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
672 template <typename Configuration>
673 struct decklink_consumer_proxy : public core::frame_consumer
675 core::monitor::subject monitor_subject_;
676 const configuration config_;
677 std::unique_ptr<decklink_consumer<Configuration>> consumer_;
678 core::video_format_desc format_desc_;
682 decklink_consumer_proxy(const configuration& config)
684 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
686 auto ctx = core::diagnostics::call_context::for_thread();
687 executor_.begin_invoke([=]
689 core::diagnostics::call_context::for_thread() = ctx;
694 ~decklink_consumer_proxy()
705 void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
707 format_desc_ = format_desc;
711 consumer_.reset(new decklink_consumer<Configuration>(config_, format_desc, channel_layout, channel_index));
715 std::future<bool> send(core::const_frame frame) override
717 return consumer_->send(frame);
720 std::wstring print() const override
722 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
725 std::wstring name() const override
730 boost::property_tree::wptree info() const override
732 boost::property_tree::wptree info;
733 info.add(L"type", L"decklink");
734 info.add(L"key-only", config_.key_only);
735 info.add(L"device", config_.device_index);
737 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
739 info.add(L"key-device", config_.key_device_index());
742 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
743 info.add(L"embedded-audio", config_.embedded_audio);
744 info.add(L"presentation-frame-age", presentation_frame_age_millis());
745 //info.add(L"internal-key", config_.internal_key);
749 int buffer_depth() const override
751 return config_.buffer_depth() + 2;
754 int index() const override
756 return 300 + config_.device_index;
759 int64_t presentation_frame_age_millis() const override
761 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
764 core::monitor::subject& monitor_output()
766 return monitor_subject_;
770 const software_version<3>& get_driver_version()
772 static software_version<3> version(u8(get_version()));
777 const software_version<3> get_new_configuration_api_version()
779 static software_version<3> NEW_CONFIGURATION_API("10.2");
781 return NEW_CONFIGURATION_API;
784 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
786 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
787 sink.syntax(L"DECKLINK "
788 L"{[device_index:int]|1} "
789 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
790 L"{[low_latency:LOW_LATENCY]} "
791 L"{[embedded_audio:EMBEDDED_AUDIO]} "
792 L"{[key_only:KEY_ONLY]} "
793 L"{CHANNEL_LAYOUT [channel_layout:string]}");
794 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
796 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
798 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
799 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
800 ->item(L"low_latency", L"Tries to enable low latency if given.")
801 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
803 L" will extract only the alpha channel from the "
804 L"channel. This is useful when you have two SDI video cards, and neither has native support "
805 L"for separate fill/key output")
806 ->item(L"channel_layout", L"If specified, overrides the audio channel layout used by the channel.");
807 sink.para()->text(L"Examples:");
808 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
809 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
810 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
812 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
813 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.");
815 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
816 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
819 spl::shared_ptr<core::frame_consumer> create_consumer(
820 const std::vector<std::wstring>& params, core::interaction_sink*)
822 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
823 return core::frame_consumer::empty();
825 configuration config;
827 if (params.size() > 1)
828 config.device_index = boost::lexical_cast<int>(params.at(1));
830 if (contains_param(L"INTERNAL_KEY", params))
831 config.keyer = configuration::keyer_t::internal_keyer;
832 else if (contains_param(L"EXTERNAL_KEY", params))
833 config.keyer = configuration::keyer_t::external_keyer;
834 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
835 config.keyer = configuration::keyer_t::external_separate_device_keyer;
837 config.keyer = configuration::keyer_t::default_keyer;
839 if (contains_param(L"LOW_LATENCY", params))
840 config.latency = configuration::latency_t::low_latency;
842 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
843 config.key_only = contains_param(L"KEY_ONLY", params);
845 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params);
847 if (!channel_layout.empty())
849 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
852 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout + L" not found."));
854 config.out_channel_layout = *found_layout;
857 bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
859 if (old_configuration_api)
860 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
862 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
865 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
866 const boost::property_tree::wptree& ptree, core::interaction_sink*)
868 configuration config;
870 auto keyer = ptree.get(L"keyer", L"default");
871 if(keyer == L"external")
872 config.keyer = configuration::keyer_t::external_keyer;
873 else if(keyer == L"internal")
874 config.keyer = configuration::keyer_t::internal_keyer;
875 else if (keyer == L"external_separate_device")
876 config.keyer = configuration::keyer_t::external_separate_device_keyer;
878 auto latency = ptree.get(L"latency", L"default");
879 if(latency == L"low")
880 config.latency = configuration::latency_t::low_latency;
881 else if(latency == L"normal")
882 config.latency = configuration::latency_t::normal_latency;
884 auto channel_layout = ptree.get_optional<std::wstring>(L"channel-layout");
888 CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
890 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
893 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
895 config.out_channel_layout = *found_layout;
898 config.key_only = ptree.get(L"key-only", config.key_only);
899 config.device_index = ptree.get(L"device", config.device_index);
900 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
901 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
902 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
904 bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
906 if (old_configuration_api)
907 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
909 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
915 ##############################################################################
921 BMD Developer Support
922 developer@blackmagic-design.com
924 -----------------------------------------------------------------------------
926 Thanks for your inquiry. The minimum number of frames that you can preroll
927 for scheduled playback is three frames for video and four frames for audio.
928 As you mentioned if you preroll less frames then playback will not start or
929 playback will be very sporadic. From our experience with Media Express, we
930 recommended that at least seven frames are prerolled for smooth playback.
932 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
933 There can be around 3 frames worth of latency on scheduled output.
934 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
935 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
936 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
937 guarantee that the provided frame will be output as soon the previous
938 frame output has been completed.
939 ################################################################################
943 ##############################################################################
944 Async DMA Transfer without redundant copying
949 BMD Developer Support
950 developer@blackmagic-design.com
952 -----------------------------------------------------------------------------
954 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
955 and providing a pointer to your video buffer when GetBytes() is called.
956 This may help to keep copying to a minimum. Please ensure that the pixel
957 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
958 have to colourspace convert which may result in additional copying.
959 ################################################################################