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/circular_buffer.hpp>
56 #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 = false;
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)
228 // IDecklinkVideoFrame
230 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
231 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
232 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
233 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
234 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
236 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
240 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
242 data_.resize(format_desc_.size);
243 *buffer = data_.data();
249 data_.resize(frame_.image_data().size());
250 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
252 *buffer = data_.data();
256 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
260 data_.resize(frame_.image_data().size());
261 std::memcpy(data_.data(), *buffer, frame_.image_data().size());
262 *buffer = data_.data();
268 CASPAR_LOG_CURRENT_EXCEPTION();
275 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
276 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
280 const core::audio_buffer& audio_data()
282 return frame_.audio_data();
285 int64_t get_age_millis() const
287 return frame_.get_age_millis();
291 template <typename Configuration>
292 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
294 const configuration config_;
295 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
296 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
297 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
298 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
299 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
300 tbb::atomic<int64_t> current_presentation_delay_;
301 tbb::atomic<int64_t> scheduled_frames_completed_;
303 key_video_context(const configuration& config, const std::wstring& print)
306 current_presentation_delay_ = 0;
307 scheduled_frames_completed_ = 0;
309 set_latency(configuration_, config.latency, print);
310 set_keyer(attributes_, keyer_, config.keyer, print);
312 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
313 CASPAR_THROW_EXCEPTION(caspar_exception()
314 << msg_info(print + L" Failed to set key playback completion callback.")
315 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
318 template<typename Print>
319 void enable_video(BMDDisplayMode display_mode, const Print& print)
321 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
322 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable key video output."));
324 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
325 CASPAR_THROW_EXCEPTION(caspar_exception()
326 << msg_info(print() + L" Failed to set key playback completion callback.")
327 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
330 virtual ~key_video_context()
334 output_->StopScheduledPlayback(0, nullptr, 0);
335 output_->DisableVideoOutput();
339 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
340 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
341 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
343 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
348 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
349 IDeckLinkVideoFrame* completed_frame,
350 BMDOutputFrameCompletionResult result)
352 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
353 current_presentation_delay_ = dframe->get_age_millis();
354 ++scheduled_frames_completed_;
356 // Let the fill callback keep the pace, so no scheduling here.
362 template <typename Configuration>
363 struct decklink_consumer : public IDeckLinkVideoOutputCallback, boost::noncopyable
365 const int channel_index_;
366 const configuration config_;
368 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
369 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
370 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
371 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
372 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
374 tbb::spin_mutex exception_mutex_;
375 std::exception_ptr exception_;
377 tbb::atomic<bool> is_running_;
379 const std::wstring model_name_ = get_model_name(decklink_);
380 bool will_attempt_dma_;
381 const core::video_format_desc format_desc_;
382 const core::audio_channel_layout in_channel_layout_;
383 const core::audio_channel_layout out_channel_layout_ = config_.get_adjusted_layout(in_channel_layout_);
384 core::audio_channel_remapper channel_remapper_ { in_channel_layout_, out_channel_layout_ };
385 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
387 long long video_scheduled_ = 0;
388 long long audio_scheduled_ = 0;
390 int preroll_count_ = 0;
392 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1u };
394 tbb::concurrent_bounded_queue<core::const_frame> frame_buffer_;
395 caspar::semaphore ready_for_new_frames_ { 0 };
397 spl::shared_ptr<diagnostics::graph> graph_;
398 caspar::timer tick_timer_;
399 reference_signal_detector reference_signal_detector_ { output_ };
400 tbb::atomic<int64_t> current_presentation_delay_;
401 tbb::atomic<int64_t> scheduled_frames_completed_;
402 std::unique_ptr<key_video_context<Configuration>> key_context_;
406 const configuration& config,
407 const core::video_format_desc& format_desc,
408 const core::audio_channel_layout& in_channel_layout,
410 : channel_index_(channel_index)
412 , format_desc_(format_desc)
413 , in_channel_layout_(in_channel_layout)
416 current_presentation_delay_ = 0;
417 scheduled_frames_completed_ = 0;
419 frame_buffer_.set_capacity(1);
421 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
422 key_context_.reset(new key_video_context<Configuration>(config, print()));
424 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
425 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
426 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
427 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
428 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
429 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
433 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
436 graph_->set_text(print());
437 diagnostics::register_graph(graph_);
439 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, will_attempt_dma_));
441 if(config.embedded_audio)
444 set_latency(configuration_, config.latency, print());
445 set_keyer(attributes_, keyer_, config.keyer, print());
447 if(config.embedded_audio)
448 output_->BeginAudioPreroll();
450 for (int n = 0; n < buffer_size_; ++n)
452 if (config.embedded_audio)
453 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[n % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
455 schedule_next_video(core::const_frame::empty());
458 if (config.embedded_audio)
460 // Preroll one extra frame worth of audio
461 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[buffer_size_ % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
462 output_->EndAudioPreroll();
471 frame_buffer_.try_push(core::const_frame::empty());
473 if(output_ != nullptr)
475 output_->StopScheduledPlayback(0, nullptr, 0);
476 if(config_.embedded_audio)
477 output_->DisableAudioOutput();
478 output_->DisableVideoOutput();
484 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
485 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable audio output."));
487 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
490 void enable_video(BMDDisplayMode display_mode)
492 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
493 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable fill video output."));
495 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
496 CASPAR_THROW_EXCEPTION(caspar_exception()
497 << msg_info(print() + L" Failed to set fill playback completion callback.")
498 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
501 key_context_->enable_video(display_mode, [this]() { return print(); });
504 void start_playback()
506 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
507 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule fill playback."));
509 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
510 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule key playback."));
513 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
514 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
515 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
517 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
520 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
524 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
531 auto tick_time = tick_timer_.elapsed()*format_desc_.fps * 0.5;
532 graph_->set_value("tick-time", tick_time);
533 tick_timer_.restart();
535 reference_signal_detector_.detect_change([this]() { return print(); });
537 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
538 current_presentation_delay_ = dframe->get_age_millis();
539 ++scheduled_frames_completed_;
545 scheduled_frames_completed_
546 - key_context_->scheduled_frames_completed_)
549 if(result == bmdOutputFrameDisplayedLate)
551 graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
552 video_scheduled_ += format_desc_.duration;
553 audio_scheduled_ += dframe->audio_data().size() / in_channel_layout_.num_channels;
555 else if(result == bmdOutputFrameDropped)
556 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
557 else if(result == bmdOutputFrameFlushed)
558 graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
561 output_->GetBufferedVideoFrameCount(&buffered);
562 graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
564 if (config_.embedded_audio)
566 output_->GetBufferedAudioSampleFrameCount(&buffered);
567 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
570 auto frame = core::const_frame::empty();
572 frame_buffer_.pop(frame);
573 ready_for_new_frames_.release();
578 if (config_.embedded_audio)
579 schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
581 schedule_next_video(frame);
585 lock(exception_mutex_, [&]
587 exception_ = std::current_exception();
596 void schedule_next_audio(const T& audio_data)
598 auto sample_frame_count = static_cast<int>(audio_data.size()/out_channel_layout_.num_channels);
600 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
602 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
603 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
605 audio_scheduled_ += sample_frame_count;
608 void schedule_next_video(core::const_frame frame)
612 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true, will_attempt_dma_));
613 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
614 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
617 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only, will_attempt_dma_));
618 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
619 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
621 video_scheduled_ += format_desc_.duration;
624 std::future<bool> send(core::const_frame frame)
626 auto exception = lock(exception_mutex_, [&]
631 if(exception != nullptr)
632 std::rethrow_exception(exception);
635 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Is not running."));
637 frame_buffer_.push(frame);
639 auto send_completion = spl::make_shared<std::promise<bool>>();
641 ready_for_new_frames_.acquire(1, [send_completion]
643 send_completion->set_value(true);
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" [" + std::to_wstring(channel_index_)+L"-" +
653 std::to_wstring(config_.device_index) +
655 std::to_wstring(config_.key_device_index()) +
657 format_desc_.name + L"]";
659 return model_name_ + L" [" + std::to_wstring(channel_index_)+L"-" +
660 std::to_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[" + std::to_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() override
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*, std::vector<spl::shared_ptr<core::video_channel>> channels)
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 = std::stoi(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*, std::vector<spl::shared_ptr<core::video_channel>> channels)
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 ################################################################################