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"
28 #include "../decklink_api.h"
30 #include <core/frame/frame.h>
31 #include <core/frame/audio_channel_layout.h>
32 #include <core/mixer/audio/audio_mixer.h>
33 #include <core/consumer/frame_consumer.h>
34 #include <core/diagnostics/call_context.h>
35 #include <core/help/help_sink.h>
36 #include <core/help/help_repository.h>
38 #include <common/executor.h>
39 #include <common/lock.h>
40 #include <common/diagnostics/graph.h>
41 #include <common/except.h>
42 #include <common/memshfl.h>
43 #include <common/memcpy.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>
51 #include <tbb/concurrent_queue.h>
53 #include <common/assert.h>
54 #include <boost/lexical_cast.hpp>
55 #include <boost/circular_buffer.hpp>
56 #include <boost/property_tree/ptree.hpp>
58 namespace caspar { namespace decklink {
66 external_separate_device_keyer,
67 default_keyer = external_keyer
74 default_latency = normal_latency
78 int key_device_idx = 0;
79 bool embedded_audio = true;
80 keyer_t keyer = keyer_t::default_keyer;
81 latency_t latency = latency_t::default_latency;
82 bool key_only = false;
83 int base_buffer_depth = 3;
84 core::audio_channel_layout out_channel_layout = core::audio_channel_layout::invalid();
86 int buffer_depth() const
88 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
91 int key_device_index() const
93 return key_device_idx == 0 ? device_index + 1 : key_device_idx;
96 core::audio_channel_layout get_adjusted_layout(const core::audio_channel_layout& in_layout) const
98 auto adjusted = out_channel_layout == core::audio_channel_layout::invalid() ? in_layout : out_channel_layout;
100 if (adjusted.num_channels == 1) // Duplicate mono-signal into both left and right.
102 adjusted.num_channels = 2;
103 adjusted.channel_order.push_back(adjusted.channel_order.at(0)); // Usually FC -> FC FC
105 else if (adjusted.num_channels == 2)
107 adjusted.num_channels = 2;
109 else if (adjusted.num_channels <= 8)
111 adjusted.num_channels = 8;
113 else // Over 8 always pad to 16 or drop >16
115 adjusted.num_channels = 16;
123 const com_iface_ptr<IDeckLinkConfiguration>& config,
124 configuration::latency_t latency,
125 const std::wstring& print)
127 if (latency == configuration::latency_t::low_latency)
129 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
130 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";
132 else if (latency == configuration::latency_t::normal_latency)
134 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
135 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";
140 const com_iface_ptr<IDeckLinkAttributes>& attributes,
141 const com_iface_ptr<IDeckLinkKeyer>& decklink_keyer,
142 configuration::keyer_t keyer,
143 const std::wstring& print)
145 if (keyer == configuration::keyer_t::internal_keyer)
148 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
149 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
150 else if (FAILED(decklink_keyer->Enable(FALSE)))
151 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
152 else if (FAILED(decklink_keyer->SetLevel(255)))
153 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
155 CASPAR_LOG(info) << print << L" Enabled internal keyer.";
157 else if (keyer == configuration::keyer_t::external_keyer)
160 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
161 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
162 else if (FAILED(decklink_keyer->Enable(TRUE)))
163 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
164 else if (FAILED(decklink_keyer->SetLevel(255)))
165 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
167 CASPAR_LOG(info) << print << L" Enabled external keyer.";
171 class decklink_frame : public IDeckLinkVideoFrame
173 tbb::atomic<int> ref_count_;
174 core::const_frame frame_;
175 const core::video_format_desc format_desc_;
177 const bool key_only_;
178 cache_aligned_vector<no_init_proxy<uint8_t>> data_;
180 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
182 , format_desc_(format_desc)
183 , key_only_(key_only)
190 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
192 return E_NOINTERFACE;
195 virtual ULONG STDMETHODCALLTYPE AddRef()
200 virtual ULONG STDMETHODCALLTYPE Release()
202 if(--ref_count_ == 0)
207 // IDecklinkVideoFrame
209 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
210 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
211 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
212 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
213 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
215 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
219 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
221 data_.resize(format_desc_.size);
222 *buffer = data_.data();
228 data_.resize(frame_.image_data().size());
229 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
231 *buffer = data_.data();
235 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
237 #if !defined(_MSC_VER)
238 // On Linux Decklink cannot DMA transfer from memory returned by glMapBuffer
239 data_.resize(frame_.image_data().size());
240 fast_memcpy(data_.data(), *buffer, frame_.image_data().size());
241 *buffer = data_.data();
247 CASPAR_LOG_CURRENT_EXCEPTION();
254 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
255 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
259 const core::audio_buffer& audio_data()
261 return frame_.audio_data();
264 int64_t get_age_millis() const
266 return frame_.get_age_millis();
270 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
272 const configuration config_;
273 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
274 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
275 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
276 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
277 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
278 tbb::atomic<int64_t> current_presentation_delay_;
279 tbb::atomic<int64_t> scheduled_frames_completed_;
281 key_video_context(const configuration& config, const std::wstring& print)
284 current_presentation_delay_ = 0;
285 scheduled_frames_completed_ = 0;
287 set_latency(configuration_, config.latency, print);
288 set_keyer(attributes_, keyer_, config.keyer, print);
290 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
291 CASPAR_THROW_EXCEPTION(caspar_exception()
292 << msg_info(print + L" Failed to set key playback completion callback.")
293 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
296 template<typename Print>
297 void enable_video(BMDDisplayMode display_mode, const Print& print)
299 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
300 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable key video output."));
302 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
303 CASPAR_THROW_EXCEPTION(caspar_exception()
304 << msg_info(print() + L" Failed to set key playback completion callback.")
305 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
308 virtual ~key_video_context()
312 output_->StopScheduledPlayback(0, nullptr, 0);
313 output_->DisableVideoOutput();
317 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
318 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
319 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
321 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
326 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
327 IDeckLinkVideoFrame* completed_frame,
328 BMDOutputFrameCompletionResult result)
330 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
331 current_presentation_delay_ = dframe->get_age_millis();
332 ++scheduled_frames_completed_;
334 // Let the fill callback keep the pace, so no scheduling here.
340 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
342 const int channel_index_;
343 const configuration config_;
345 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
346 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
347 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
348 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
349 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
351 tbb::spin_mutex exception_mutex_;
352 std::exception_ptr exception_;
354 tbb::atomic<bool> is_running_;
356 const std::wstring model_name_ = get_model_name(decklink_);
357 const core::video_format_desc format_desc_;
358 const core::audio_channel_layout in_channel_layout_;
359 const core::audio_channel_layout out_channel_layout_ = config_.get_adjusted_layout(in_channel_layout_);
360 core::audio_channel_remapper channel_remapper_ { in_channel_layout_, out_channel_layout_ };
361 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
363 long long video_scheduled_ = 0;
364 long long audio_scheduled_ = 0;
366 int preroll_count_ = 0;
368 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
370 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
371 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
373 spl::shared_ptr<diagnostics::graph> graph_;
374 caspar::timer tick_timer_;
375 retry_task<bool> send_completion_;
376 reference_signal_detector reference_signal_detector_ { output_ };
377 tbb::atomic<int64_t> current_presentation_delay_;
378 tbb::atomic<int64_t> scheduled_frames_completed_;
379 std::unique_ptr<key_video_context> key_context_;
383 const configuration& config,
384 const core::video_format_desc& format_desc,
385 const core::audio_channel_layout& in_channel_layout,
387 : channel_index_(channel_index)
389 , format_desc_(format_desc)
390 , in_channel_layout_(in_channel_layout)
393 current_presentation_delay_ = 0;
394 scheduled_frames_completed_ = 0;
396 video_frame_buffer_.set_capacity(1);
398 // Blackmagic calls RenderAudioSamples() 50 times per second
399 // regardless of video mode so we sometimes need to give them
400 // samples from 2 frames in order to keep up
401 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
403 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
404 key_context_.reset(new key_video_context(config, print()));
406 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
407 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
408 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
409 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
410 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
411 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
415 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
418 graph_->set_text(print());
419 diagnostics::register_graph(graph_);
421 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
423 if(config.embedded_audio)
426 set_latency(configuration_, config.latency, print());
427 set_keyer(attributes_, keyer_, config.keyer, print());
429 if(config.embedded_audio)
430 output_->BeginAudioPreroll();
432 for(int n = 0; n < buffer_size_; ++n)
433 schedule_next_video(core::const_frame::empty());
435 if(!config.embedded_audio)
442 video_frame_buffer_.try_push(core::const_frame::empty());
443 audio_frame_buffer_.try_push(core::const_frame::empty());
445 if(output_ != nullptr)
447 output_->StopScheduledPlayback(0, nullptr, 0);
448 if(config_.embedded_audio)
449 output_->DisableAudioOutput();
450 output_->DisableVideoOutput();
456 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
457 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable audio output."));
459 if(FAILED(output_->SetAudioCallback(this)))
460 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not set audio callback."));
462 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
465 void enable_video(BMDDisplayMode display_mode)
467 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
468 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable fill video output."));
470 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
471 CASPAR_THROW_EXCEPTION(caspar_exception()
472 << msg_info(print() + L" Failed to set fill playback completion callback.")
473 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
476 key_context_->enable_video(display_mode, [this]() { return print(); });
479 void start_playback()
481 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
482 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule fill playback."));
484 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
485 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule key playback."));
488 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
489 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
490 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
492 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
495 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
499 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
506 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
507 current_presentation_delay_ = dframe->get_age_millis();
508 ++scheduled_frames_completed_;
514 scheduled_frames_completed_
515 - key_context_->scheduled_frames_completed_)
518 if(result == bmdOutputFrameDisplayedLate)
520 graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
521 video_scheduled_ += format_desc_.duration;
522 audio_scheduled_ += dframe->audio_data().size() / out_channel_layout_.num_channels;
523 //++video_scheduled_;
524 //audio_scheduled_ += format_desc_.audio_cadence[0];
525 //++audio_scheduled_;
527 else if(result == bmdOutputFrameDropped)
528 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
529 else if(result == bmdOutputFrameFlushed)
530 graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
533 output_->GetBufferedVideoFrameCount(&buffered);
534 graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
536 auto frame = core::const_frame::empty();
537 video_frame_buffer_.pop(frame);
538 send_completion_.try_completion();
539 schedule_next_video(frame);
543 lock(exception_mutex_, [&]
545 exception_ = std::current_exception();
553 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
562 if(++preroll_count_ >= buffer_size_)
564 output_->EndAudioPreroll();
569 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
574 auto frame = core::const_frame::empty();
576 while(audio_frame_buffer_.try_pop(frame))
579 output_->GetBufferedAudioSampleFrameCount(&buffered);
580 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
582 send_completion_.try_completion();
583 schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
589 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
590 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));
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));
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;
625 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
626 tick_timer_.restart();
628 reference_signal_detector_.detect_change([this]() { return print(); });
631 std::future<bool> send(core::const_frame frame)
633 auto exception = lock(exception_mutex_, [&]
638 if(exception != nullptr)
639 std::rethrow_exception(exception);
642 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Is not running."));
644 bool audio_ready = !config_.embedded_audio;
645 bool video_ready = false;
647 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
650 audio_ready = audio_frame_buffer_.try_push(frame);
653 video_ready = video_frame_buffer_.try_push(frame);
655 if (audio_ready && video_ready)
658 return boost::optional<bool>();
662 return make_ready_future(true);
664 send_completion_.set_task(enqueue_task);
666 return send_completion_.get_future();
669 std::wstring print() const
671 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
672 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
673 boost::lexical_cast<std::wstring>(config_.device_index) +
675 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
677 format_desc_.name + L"]";
679 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
680 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
684 struct decklink_consumer_proxy : public core::frame_consumer
686 core::monitor::subject monitor_subject_;
687 const configuration config_;
688 std::unique_ptr<decklink_consumer> consumer_;
689 core::video_format_desc format_desc_;
693 decklink_consumer_proxy(const configuration& config)
695 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
697 auto ctx = core::diagnostics::call_context::for_thread();
698 executor_.begin_invoke([=]
700 core::diagnostics::call_context::for_thread() = ctx;
705 ~decklink_consumer_proxy()
716 void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
718 format_desc_ = format_desc;
722 consumer_.reset(new decklink_consumer(config_, format_desc, channel_layout, channel_index));
726 std::future<bool> send(core::const_frame frame) override
728 return consumer_->send(frame);
731 std::wstring print() const override
733 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
736 std::wstring name() const override
741 boost::property_tree::wptree info() const override
743 boost::property_tree::wptree info;
744 info.add(L"type", L"decklink");
745 info.add(L"key-only", config_.key_only);
746 info.add(L"device", config_.device_index);
748 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
750 info.add(L"key-device", config_.key_device_index());
753 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
754 info.add(L"embedded-audio", config_.embedded_audio);
755 info.add(L"presentation-frame-age", presentation_frame_age_millis());
756 //info.add(L"internal-key", config_.internal_key);
760 int buffer_depth() const override
762 return config_.buffer_depth() + 2;
765 int index() const override
767 return 300 + config_.device_index;
770 int64_t presentation_frame_age_millis() const override
772 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
775 core::monitor::subject& monitor_output()
777 return monitor_subject_;
781 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
783 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
784 sink.syntax(L"DECKLINK "
785 L"{[device_index:int]|1} "
786 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
787 L"{[low_latency:LOW_LATENCY]} "
788 L"{[embedded_audio:EMBEDDED_AUDIO]} "
789 L"{[key_only:KEY_ONLY]} "
790 L"{CHANNEL_LAYOUT [channel_layout:string]}");
791 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
793 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
795 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
796 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
797 ->item(L"low_latency", L"Tries to enable low latency if given.")
798 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
800 L" will extract only the alpha channel from the "
801 L"channel. This is useful when you have two SDI video cards, and neither has native support "
802 L"for separate fill/key output")
803 ->item(L"channel_layout", L"If specified, overrides the audio channel layout used by the channel.");
804 sink.para()->text(L"Examples:");
805 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
806 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
807 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
809 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
810 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.");
812 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
813 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
816 spl::shared_ptr<core::frame_consumer> create_consumer(
817 const std::vector<std::wstring>& params, core::interaction_sink*)
819 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
820 return core::frame_consumer::empty();
822 configuration config;
824 if (params.size() > 1)
825 config.device_index = boost::lexical_cast<int>(params.at(1));
827 if (contains_param(L"INTERNAL_KEY", params))
828 config.keyer = configuration::keyer_t::internal_keyer;
829 else if (contains_param(L"EXTERNAL_KEY", params))
830 config.keyer = configuration::keyer_t::external_keyer;
831 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
832 config.keyer = configuration::keyer_t::external_separate_device_keyer;
834 config.keyer = configuration::keyer_t::default_keyer;
836 if (contains_param(L"LOW_LATENCY", params))
837 config.latency = configuration::latency_t::low_latency;
839 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
840 config.key_only = contains_param(L"KEY_ONLY", params);
842 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params);
844 if (!channel_layout.empty())
846 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
849 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout + L" not found."));
851 config.out_channel_layout = *found_layout;
854 return spl::make_shared<decklink_consumer_proxy>(config);
857 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
858 const boost::property_tree::wptree& ptree, core::interaction_sink*)
860 configuration config;
862 auto keyer = ptree.get(L"keyer", L"default");
863 if(keyer == L"external")
864 config.keyer = configuration::keyer_t::external_keyer;
865 else if(keyer == L"internal")
866 config.keyer = configuration::keyer_t::internal_keyer;
867 else if (keyer == L"external_separate_device")
868 config.keyer = configuration::keyer_t::external_separate_device_keyer;
870 auto latency = ptree.get(L"latency", L"default");
871 if(latency == L"low")
872 config.latency = configuration::latency_t::low_latency;
873 else if(latency == L"normal")
874 config.latency = configuration::latency_t::normal_latency;
876 auto channel_layout = ptree.get_optional<std::wstring>(L"channel-layout");
880 CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
882 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
885 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
887 config.out_channel_layout = *found_layout;
890 config.key_only = ptree.get(L"key-only", config.key_only);
891 config.device_index = ptree.get(L"device", config.device_index);
892 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
893 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
894 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
896 return spl::make_shared<decklink_consumer_proxy>(config);
902 ##############################################################################
908 BMD Developer Support
909 developer@blackmagic-design.com
911 -----------------------------------------------------------------------------
913 Thanks for your inquiry. The minimum number of frames that you can preroll
914 for scheduled playback is three frames for video and four frames for audio.
915 As you mentioned if you preroll less frames then playback will not start or
916 playback will be very sporadic. From our experience with Media Express, we
917 recommended that at least seven frames are prerolled for smooth playback.
919 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
920 There can be around 3 frames worth of latency on scheduled output.
921 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
922 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
923 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
924 guarantee that the provided frame will be output as soon the previous
925 frame output has been completed.
926 ################################################################################
930 ##############################################################################
931 Async DMA Transfer without redundant copying
936 BMD Developer Support
937 developer@blackmagic-design.com
939 -----------------------------------------------------------------------------
941 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
942 and providing a pointer to your video buffer when GetBytes() is called.
943 This may help to keep copying to a minimum. Please ensure that the pixel
944 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
945 have to colourspace convert which may result in additional copying.
946 ################################################################################