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 = false;
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)
231 // IDecklinkVideoFrame
233 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
234 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
235 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
236 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
237 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
239 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
243 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
245 data_.resize(format_desc_.size);
246 *buffer = data_.data();
252 data_.resize(frame_.image_data().size());
253 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
255 *buffer = data_.data();
259 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
263 data_.resize(frame_.image_data().size());
264 fast_memcpy(data_.data(), *buffer, frame_.image_data().size());
265 *buffer = data_.data();
271 CASPAR_LOG_CURRENT_EXCEPTION();
278 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
279 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
283 const core::audio_buffer& audio_data()
285 return frame_.audio_data();
288 int64_t get_age_millis() const
290 return frame_.get_age_millis();
294 template <typename Configuration>
295 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
297 const configuration config_;
298 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
299 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
300 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
301 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
302 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
303 tbb::atomic<int64_t> current_presentation_delay_;
304 tbb::atomic<int64_t> scheduled_frames_completed_;
306 key_video_context(const configuration& config, const std::wstring& print)
309 current_presentation_delay_ = 0;
310 scheduled_frames_completed_ = 0;
312 set_latency(configuration_, config.latency, print);
313 set_keyer(attributes_, keyer_, config.keyer, print);
315 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
316 CASPAR_THROW_EXCEPTION(caspar_exception()
317 << msg_info(print + L" Failed to set key playback completion callback.")
318 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
321 template<typename Print>
322 void enable_video(BMDDisplayMode display_mode, const Print& print)
324 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
325 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable key video output."));
327 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
328 CASPAR_THROW_EXCEPTION(caspar_exception()
329 << msg_info(print() + L" Failed to set key playback completion callback.")
330 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
333 virtual ~key_video_context()
337 output_->StopScheduledPlayback(0, nullptr, 0);
338 output_->DisableVideoOutput();
342 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
343 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
344 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
346 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
351 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
352 IDeckLinkVideoFrame* completed_frame,
353 BMDOutputFrameCompletionResult result)
355 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
356 current_presentation_delay_ = dframe->get_age_millis();
357 ++scheduled_frames_completed_;
359 // Let the fill callback keep the pace, so no scheduling here.
365 template <typename Configuration>
366 struct decklink_consumer : public IDeckLinkVideoOutputCallback, boost::noncopyable
368 const int channel_index_;
369 const configuration config_;
371 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
372 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
373 com_iface_ptr<Configuration> configuration_ = iface_cast<Configuration>(decklink_);
374 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_, true);
375 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
377 tbb::spin_mutex exception_mutex_;
378 std::exception_ptr exception_;
380 tbb::atomic<bool> is_running_;
382 const std::wstring model_name_ = get_model_name(decklink_);
383 bool will_attempt_dma_;
384 const core::video_format_desc format_desc_;
385 const core::audio_channel_layout in_channel_layout_;
386 const core::audio_channel_layout out_channel_layout_ = config_.get_adjusted_layout(in_channel_layout_);
387 core::audio_channel_remapper channel_remapper_ { in_channel_layout_, out_channel_layout_ };
388 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
390 long long video_scheduled_ = 0;
391 long long audio_scheduled_ = 0;
393 int preroll_count_ = 0;
395 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
397 tbb::concurrent_bounded_queue<core::const_frame> frame_buffer_;
399 spl::shared_ptr<diagnostics::graph> graph_;
400 caspar::timer tick_timer_;
401 boost::mutex send_completion_mutex_;
402 std::packaged_task<bool ()> send_completion_;
403 reference_signal_detector reference_signal_detector_ { output_ };
404 tbb::atomic<int64_t> current_presentation_delay_;
405 tbb::atomic<int64_t> scheduled_frames_completed_;
406 std::unique_ptr<key_video_context<Configuration>> key_context_;
410 const configuration& config,
411 const core::video_format_desc& format_desc,
412 const core::audio_channel_layout& in_channel_layout,
414 : channel_index_(channel_index)
416 , format_desc_(format_desc)
417 , in_channel_layout_(in_channel_layout)
420 current_presentation_delay_ = 0;
421 scheduled_frames_completed_ = 0;
423 frame_buffer_.set_capacity(1);
425 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
426 key_context_.reset(new key_video_context<Configuration>(config, print()));
428 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
429 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
430 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
431 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
432 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
433 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
437 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
440 graph_->set_text(print());
441 diagnostics::register_graph(graph_);
443 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, will_attempt_dma_));
445 if(config.embedded_audio)
448 set_latency(configuration_, config.latency, print());
449 set_keyer(attributes_, keyer_, config.keyer, print());
451 if(config.embedded_audio)
452 output_->BeginAudioPreroll();
454 for (int n = 0; n < buffer_size_; ++n)
456 if (config.embedded_audio)
457 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[n % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
459 schedule_next_video(core::const_frame::empty());
462 if (config.embedded_audio)
464 // Preroll one extra frame worth of audio
465 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[buffer_size_ % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
466 output_->EndAudioPreroll();
475 frame_buffer_.try_push(core::const_frame::empty());
477 if(output_ != nullptr)
479 output_->StopScheduledPlayback(0, nullptr, 0);
480 if(config_.embedded_audio)
481 output_->DisableAudioOutput();
482 output_->DisableVideoOutput();
488 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
489 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable audio output."));
491 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
494 void enable_video(BMDDisplayMode display_mode)
496 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
497 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable fill video output."));
499 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
500 CASPAR_THROW_EXCEPTION(caspar_exception()
501 << msg_info(print() + L" Failed to set fill playback completion callback.")
502 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
505 key_context_->enable_video(display_mode, [this]() { return print(); });
508 void start_playback()
510 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
511 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule fill playback."));
513 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
514 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule key playback."));
517 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
518 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
519 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
521 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
524 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
528 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
535 auto tick_time = tick_timer_.elapsed()*format_desc_.fps * 0.5;
536 graph_->set_value("tick-time", tick_time);
537 tick_timer_.restart();
539 reference_signal_detector_.detect_change([this]() { return print(); });
541 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
542 current_presentation_delay_ = dframe->get_age_millis();
543 ++scheduled_frames_completed_;
549 scheduled_frames_completed_
550 - key_context_->scheduled_frames_completed_)
553 if(result == bmdOutputFrameDisplayedLate)
555 graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
556 video_scheduled_ += format_desc_.duration;
557 audio_scheduled_ += dframe->audio_data().size() / in_channel_layout_.num_channels;
559 else if(result == bmdOutputFrameDropped)
560 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
561 else if(result == bmdOutputFrameFlushed)
562 graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
565 output_->GetBufferedVideoFrameCount(&buffered);
566 graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
568 if (config_.embedded_audio)
570 output_->GetBufferedAudioSampleFrameCount(&buffered);
571 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
574 auto frame = core::const_frame::empty();
576 frame_buffer_.pop(frame);
579 boost::lock_guard<boost::mutex> lock(send_completion_mutex_);
581 if (send_completion_.valid())
584 send_completion_ = std::packaged_task<bool()>();
591 if (config_.embedded_audio)
592 schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
594 schedule_next_video(frame);
598 lock(exception_mutex_, [&]
600 exception_ = std::current_exception();
609 void schedule_next_audio(const T& audio_data)
611 auto sample_frame_count = static_cast<int>(audio_data.size()/out_channel_layout_.num_channels);
613 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
615 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
616 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
618 audio_scheduled_ += sample_frame_count;
621 void schedule_next_video(core::const_frame frame)
625 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true, will_attempt_dma_));
626 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
627 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
630 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only, will_attempt_dma_));
631 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
632 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
634 video_scheduled_ += format_desc_.duration;
637 std::future<bool> send(core::const_frame frame)
639 auto exception = lock(exception_mutex_, [&]
644 if(exception != nullptr)
645 std::rethrow_exception(exception);
648 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Is not running."));
650 if (frame_buffer_.try_push(frame))
651 return make_ready_future(true);
653 boost::lock_guard<boost::mutex> lock(send_completion_mutex_);
655 send_completion_ = std::packaged_task<bool ()>([frame, this] () mutable -> bool
657 frame_buffer_.push(frame);
662 return send_completion_.get_future();
665 std::wstring print() const
667 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
668 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
669 boost::lexical_cast<std::wstring>(config_.device_index) +
671 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
673 format_desc_.name + L"]";
675 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
676 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
680 template <typename Configuration>
681 struct decklink_consumer_proxy : public core::frame_consumer
683 core::monitor::subject monitor_subject_;
684 const configuration config_;
685 std::unique_ptr<decklink_consumer<Configuration>> consumer_;
686 core::video_format_desc format_desc_;
690 decklink_consumer_proxy(const configuration& config)
692 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
694 auto ctx = core::diagnostics::call_context::for_thread();
695 executor_.begin_invoke([=]
697 core::diagnostics::call_context::for_thread() = ctx;
702 ~decklink_consumer_proxy()
713 void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
715 format_desc_ = format_desc;
719 consumer_.reset(new decklink_consumer<Configuration>(config_, format_desc, channel_layout, channel_index));
723 std::future<bool> send(core::const_frame frame) override
725 return consumer_->send(frame);
728 std::wstring print() const override
730 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
733 std::wstring name() const override
738 boost::property_tree::wptree info() const override
740 boost::property_tree::wptree info;
741 info.add(L"type", L"decklink");
742 info.add(L"key-only", config_.key_only);
743 info.add(L"device", config_.device_index);
745 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
747 info.add(L"key-device", config_.key_device_index());
750 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
751 info.add(L"embedded-audio", config_.embedded_audio);
752 info.add(L"presentation-frame-age", presentation_frame_age_millis());
753 //info.add(L"internal-key", config_.internal_key);
757 int buffer_depth() const override
759 return config_.buffer_depth() + 2;
762 int index() const override
764 return 300 + config_.device_index;
767 int64_t presentation_frame_age_millis() const override
769 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
772 core::monitor::subject& monitor_output()
774 return monitor_subject_;
778 const software_version<3>& get_driver_version()
780 static software_version<3> version(u8(get_version()));
785 const software_version<3> get_new_configuration_api_version()
787 static software_version<3> NEW_CONFIGURATION_API("10.2");
789 return NEW_CONFIGURATION_API;
792 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
794 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
795 sink.syntax(L"DECKLINK "
796 L"{[device_index:int]|1} "
797 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
798 L"{[low_latency:LOW_LATENCY]} "
799 L"{[embedded_audio:EMBEDDED_AUDIO]} "
800 L"{[key_only:KEY_ONLY]} "
801 L"{CHANNEL_LAYOUT [channel_layout:string]}");
802 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
804 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
806 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
807 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
808 ->item(L"low_latency", L"Tries to enable low latency if given.")
809 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
811 L" will extract only the alpha channel from the "
812 L"channel. This is useful when you have two SDI video cards, and neither has native support "
813 L"for separate fill/key output")
814 ->item(L"channel_layout", L"If specified, overrides the audio channel layout used by the channel.");
815 sink.para()->text(L"Examples:");
816 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
817 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
818 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
820 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
821 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.");
823 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
824 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
827 spl::shared_ptr<core::frame_consumer> create_consumer(
828 const std::vector<std::wstring>& params, core::interaction_sink*, std::vector<spl::shared_ptr<core::video_channel>> channels)
830 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
831 return core::frame_consumer::empty();
833 configuration config;
835 if (params.size() > 1)
836 config.device_index = boost::lexical_cast<int>(params.at(1));
838 if (contains_param(L"INTERNAL_KEY", params))
839 config.keyer = configuration::keyer_t::internal_keyer;
840 else if (contains_param(L"EXTERNAL_KEY", params))
841 config.keyer = configuration::keyer_t::external_keyer;
842 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
843 config.keyer = configuration::keyer_t::external_separate_device_keyer;
845 config.keyer = configuration::keyer_t::default_keyer;
847 if (contains_param(L"LOW_LATENCY", params))
848 config.latency = configuration::latency_t::low_latency;
850 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
851 config.key_only = contains_param(L"KEY_ONLY", params);
853 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params);
855 if (!channel_layout.empty())
857 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
860 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout + L" not found."));
862 config.out_channel_layout = *found_layout;
865 bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
867 if (old_configuration_api)
868 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
870 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
873 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
874 const boost::property_tree::wptree& ptree, core::interaction_sink*, std::vector<spl::shared_ptr<core::video_channel>> channels)
876 configuration config;
878 auto keyer = ptree.get(L"keyer", L"default");
879 if(keyer == L"external")
880 config.keyer = configuration::keyer_t::external_keyer;
881 else if(keyer == L"internal")
882 config.keyer = configuration::keyer_t::internal_keyer;
883 else if (keyer == L"external_separate_device")
884 config.keyer = configuration::keyer_t::external_separate_device_keyer;
886 auto latency = ptree.get(L"latency", L"default");
887 if(latency == L"low")
888 config.latency = configuration::latency_t::low_latency;
889 else if(latency == L"normal")
890 config.latency = configuration::latency_t::normal_latency;
892 auto channel_layout = ptree.get_optional<std::wstring>(L"channel-layout");
896 CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
898 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
901 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
903 config.out_channel_layout = *found_layout;
906 config.key_only = ptree.get(L"key-only", config.key_only);
907 config.device_index = ptree.get(L"device", config.device_index);
908 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
909 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
910 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
912 bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
914 if (old_configuration_api)
915 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
917 return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
923 ##############################################################################
929 BMD Developer Support
930 developer@blackmagic-design.com
932 -----------------------------------------------------------------------------
934 Thanks for your inquiry. The minimum number of frames that you can preroll
935 for scheduled playback is three frames for video and four frames for audio.
936 As you mentioned if you preroll less frames then playback will not start or
937 playback will be very sporadic. From our experience with Media Express, we
938 recommended that at least seven frames are prerolled for smooth playback.
940 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
941 There can be around 3 frames worth of latency on scheduled output.
942 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
943 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
944 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
945 guarantee that the provided frame will be output as soon the previous
946 frame output has been completed.
947 ################################################################################
951 ##############################################################################
952 Async DMA Transfer without redundant copying
957 BMD Developer Support
958 developer@blackmagic-design.com
960 -----------------------------------------------------------------------------
962 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
963 and providing a pointer to your video buffer when GetBytes() is called.
964 This may help to keep copying to a minimum. Please ensure that the pixel
965 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
966 have to colourspace convert which may result in additional copying.
967 ################################################################################