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_;
179 cache_aligned_vector<no_init_proxy<uint8_t>> data_;
181 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only, bool will_attempt_dma)
183 , format_desc_(format_desc)
184 , key_only_(key_only)
188 #if !defined(_MSC_VER)
189 // On Linux Decklink cannot DMA transfer from memory returned by glMapBuffer
190 needs_to_copy_ = will_attempt_dma;
192 // On Windows it does
193 needs_to_copy_ = false;
199 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
201 return E_NOINTERFACE;
204 virtual ULONG STDMETHODCALLTYPE AddRef()
209 virtual ULONG STDMETHODCALLTYPE Release()
211 if(--ref_count_ == 0)
216 // IDecklinkVideoFrame
218 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
219 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
220 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
221 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
222 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
224 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
228 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
230 data_.resize(format_desc_.size);
231 *buffer = data_.data();
237 data_.resize(frame_.image_data().size());
238 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
240 *buffer = data_.data();
244 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
248 data_.resize(frame_.image_data().size());
249 fast_memcpy(data_.data(), *buffer, frame_.image_data().size());
250 *buffer = data_.data();
256 CASPAR_LOG_CURRENT_EXCEPTION();
263 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
264 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
268 const core::audio_buffer& audio_data()
270 return frame_.audio_data();
273 int64_t get_age_millis() const
275 return frame_.get_age_millis();
279 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
281 const configuration config_;
282 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
283 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
284 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
285 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
286 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
287 tbb::atomic<int64_t> current_presentation_delay_;
288 tbb::atomic<int64_t> scheduled_frames_completed_;
290 key_video_context(const configuration& config, const std::wstring& print)
293 current_presentation_delay_ = 0;
294 scheduled_frames_completed_ = 0;
296 set_latency(configuration_, config.latency, print);
297 set_keyer(attributes_, keyer_, config.keyer, print);
299 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
300 CASPAR_THROW_EXCEPTION(caspar_exception()
301 << msg_info(print + L" Failed to set key playback completion callback.")
302 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
305 template<typename Print>
306 void enable_video(BMDDisplayMode display_mode, const Print& print)
308 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
309 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable key video output."));
311 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
312 CASPAR_THROW_EXCEPTION(caspar_exception()
313 << msg_info(print() + L" Failed to set key playback completion callback.")
314 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
317 virtual ~key_video_context()
321 output_->StopScheduledPlayback(0, nullptr, 0);
322 output_->DisableVideoOutput();
326 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
327 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
328 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
330 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
335 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
336 IDeckLinkVideoFrame* completed_frame,
337 BMDOutputFrameCompletionResult result)
339 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
340 current_presentation_delay_ = dframe->get_age_millis();
341 ++scheduled_frames_completed_;
343 // Let the fill callback keep the pace, so no scheduling here.
349 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
351 const int channel_index_;
352 const configuration config_;
354 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
355 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
356 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
357 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
358 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
360 tbb::spin_mutex exception_mutex_;
361 std::exception_ptr exception_;
363 tbb::atomic<bool> is_running_;
365 const std::wstring model_name_ = get_model_name(decklink_);
366 bool will_attempt_dma_;
367 const core::video_format_desc format_desc_;
368 const core::audio_channel_layout in_channel_layout_;
369 const core::audio_channel_layout out_channel_layout_ = config_.get_adjusted_layout(in_channel_layout_);
370 core::audio_channel_remapper channel_remapper_ { in_channel_layout_, out_channel_layout_ };
371 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
373 long long video_scheduled_ = 0;
374 long long audio_scheduled_ = 0;
376 int preroll_count_ = 0;
378 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
380 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
381 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
383 spl::shared_ptr<diagnostics::graph> graph_;
384 caspar::timer tick_timer_;
385 retry_task<bool> send_completion_;
386 reference_signal_detector reference_signal_detector_ { output_ };
387 tbb::atomic<int64_t> current_presentation_delay_;
388 tbb::atomic<int64_t> scheduled_frames_completed_;
389 std::unique_ptr<key_video_context> key_context_;
393 const configuration& config,
394 const core::video_format_desc& format_desc,
395 const core::audio_channel_layout& in_channel_layout,
397 : channel_index_(channel_index)
399 , format_desc_(format_desc)
400 , in_channel_layout_(in_channel_layout)
403 current_presentation_delay_ = 0;
404 scheduled_frames_completed_ = 0;
406 video_frame_buffer_.set_capacity(1);
408 // Blackmagic calls RenderAudioSamples() 50 times per second
409 // regardless of video mode so we sometimes need to give them
410 // samples from 2 frames in order to keep up
411 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
413 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
414 key_context_.reset(new key_video_context(config, print()));
416 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
417 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
418 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
419 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
420 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
421 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
425 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
428 graph_->set_text(print());
429 diagnostics::register_graph(graph_);
431 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, will_attempt_dma_));
433 if(config.embedded_audio)
436 set_latency(configuration_, config.latency, print());
437 set_keyer(attributes_, keyer_, config.keyer, print());
439 if(config.embedded_audio)
440 output_->BeginAudioPreroll();
442 for(int n = 0; n < buffer_size_; ++n)
443 schedule_next_video(core::const_frame::empty());
445 if(!config.embedded_audio)
452 video_frame_buffer_.try_push(core::const_frame::empty());
453 audio_frame_buffer_.try_push(core::const_frame::empty());
455 if(output_ != nullptr)
457 output_->StopScheduledPlayback(0, nullptr, 0);
458 if(config_.embedded_audio)
459 output_->DisableAudioOutput();
460 output_->DisableVideoOutput();
466 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
467 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable audio output."));
469 if(FAILED(output_->SetAudioCallback(this)))
470 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not set audio callback."));
472 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
475 void enable_video(BMDDisplayMode display_mode)
477 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
478 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable fill video output."));
480 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
481 CASPAR_THROW_EXCEPTION(caspar_exception()
482 << msg_info(print() + L" Failed to set fill playback completion callback.")
483 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
486 key_context_->enable_video(display_mode, [this]() { return print(); });
489 void start_playback()
491 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
492 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule fill playback."));
494 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
495 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule key playback."));
498 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
499 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
500 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
502 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
505 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
509 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
516 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
517 current_presentation_delay_ = dframe->get_age_millis();
518 ++scheduled_frames_completed_;
524 scheduled_frames_completed_
525 - key_context_->scheduled_frames_completed_)
528 if(result == bmdOutputFrameDisplayedLate)
530 graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
531 video_scheduled_ += format_desc_.duration;
532 audio_scheduled_ += dframe->audio_data().size() / out_channel_layout_.num_channels;
533 //++video_scheduled_;
534 //audio_scheduled_ += format_desc_.audio_cadence[0];
535 //++audio_scheduled_;
537 else if(result == bmdOutputFrameDropped)
538 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
539 else if(result == bmdOutputFrameFlushed)
540 graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
543 output_->GetBufferedVideoFrameCount(&buffered);
544 graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
546 auto frame = core::const_frame::empty();
547 video_frame_buffer_.pop(frame);
548 send_completion_.try_completion();
549 schedule_next_video(frame);
553 lock(exception_mutex_, [&]
555 exception_ = std::current_exception();
563 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
572 if(++preroll_count_ >= buffer_size_)
574 output_->EndAudioPreroll();
579 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
584 auto frame = core::const_frame::empty();
586 while(audio_frame_buffer_.try_pop(frame))
589 output_->GetBufferedAudioSampleFrameCount(&buffered);
590 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
592 send_completion_.try_completion();
593 schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
599 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
600 exception_ = std::current_exception();
608 void schedule_next_audio(const T& audio_data)
610 auto sample_frame_count = static_cast<int>(audio_data.size()/out_channel_layout_.num_channels);
612 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
614 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
615 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
617 audio_scheduled_ += sample_frame_count;
620 void schedule_next_video(core::const_frame frame)
624 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true, will_attempt_dma_));
625 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
626 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
629 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only, will_attempt_dma_));
630 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
631 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
633 video_scheduled_ += format_desc_.duration;
635 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
636 tick_timer_.restart();
638 reference_signal_detector_.detect_change([this]() { return print(); });
641 std::future<bool> send(core::const_frame frame)
643 auto exception = lock(exception_mutex_, [&]
648 if(exception != nullptr)
649 std::rethrow_exception(exception);
652 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Is not running."));
654 bool audio_ready = !config_.embedded_audio;
655 bool video_ready = false;
657 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
660 audio_ready = audio_frame_buffer_.try_push(frame);
663 video_ready = video_frame_buffer_.try_push(frame);
665 if (audio_ready && video_ready)
668 return boost::optional<bool>();
672 return make_ready_future(true);
674 send_completion_.set_task(enqueue_task);
676 return send_completion_.get_future();
679 std::wstring print() const
681 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
682 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
683 boost::lexical_cast<std::wstring>(config_.device_index) +
685 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
687 format_desc_.name + L"]";
689 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
690 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
694 struct decklink_consumer_proxy : public core::frame_consumer
696 core::monitor::subject monitor_subject_;
697 const configuration config_;
698 std::unique_ptr<decklink_consumer> consumer_;
699 core::video_format_desc format_desc_;
703 decklink_consumer_proxy(const configuration& config)
705 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
707 auto ctx = core::diagnostics::call_context::for_thread();
708 executor_.begin_invoke([=]
710 core::diagnostics::call_context::for_thread() = ctx;
715 ~decklink_consumer_proxy()
726 void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
728 format_desc_ = format_desc;
732 consumer_.reset(new decklink_consumer(config_, format_desc, channel_layout, channel_index));
736 std::future<bool> send(core::const_frame frame) override
738 return consumer_->send(frame);
741 std::wstring print() const override
743 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
746 std::wstring name() const override
751 boost::property_tree::wptree info() const override
753 boost::property_tree::wptree info;
754 info.add(L"type", L"decklink");
755 info.add(L"key-only", config_.key_only);
756 info.add(L"device", config_.device_index);
758 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
760 info.add(L"key-device", config_.key_device_index());
763 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
764 info.add(L"embedded-audio", config_.embedded_audio);
765 info.add(L"presentation-frame-age", presentation_frame_age_millis());
766 //info.add(L"internal-key", config_.internal_key);
770 int buffer_depth() const override
772 return config_.buffer_depth() + 2;
775 int index() const override
777 return 300 + config_.device_index;
780 int64_t presentation_frame_age_millis() const override
782 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
785 core::monitor::subject& monitor_output()
787 return monitor_subject_;
791 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
793 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
794 sink.syntax(L"DECKLINK "
795 L"{[device_index:int]|1} "
796 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
797 L"{[low_latency:LOW_LATENCY]} "
798 L"{[embedded_audio:EMBEDDED_AUDIO]} "
799 L"{[key_only:KEY_ONLY]} "
800 L"{CHANNEL_LAYOUT [channel_layout:string]}");
801 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
803 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
805 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
806 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
807 ->item(L"low_latency", L"Tries to enable low latency if given.")
808 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
810 L" will extract only the alpha channel from the "
811 L"channel. This is useful when you have two SDI video cards, and neither has native support "
812 L"for separate fill/key output")
813 ->item(L"channel_layout", L"If specified, overrides the audio channel layout used by the channel.");
814 sink.para()->text(L"Examples:");
815 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
816 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
817 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
819 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
820 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.");
822 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
823 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
826 spl::shared_ptr<core::frame_consumer> create_consumer(
827 const std::vector<std::wstring>& params, core::interaction_sink*)
829 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
830 return core::frame_consumer::empty();
832 configuration config;
834 if (params.size() > 1)
835 config.device_index = boost::lexical_cast<int>(params.at(1));
837 if (contains_param(L"INTERNAL_KEY", params))
838 config.keyer = configuration::keyer_t::internal_keyer;
839 else if (contains_param(L"EXTERNAL_KEY", params))
840 config.keyer = configuration::keyer_t::external_keyer;
841 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
842 config.keyer = configuration::keyer_t::external_separate_device_keyer;
844 config.keyer = configuration::keyer_t::default_keyer;
846 if (contains_param(L"LOW_LATENCY", params))
847 config.latency = configuration::latency_t::low_latency;
849 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
850 config.key_only = contains_param(L"KEY_ONLY", params);
852 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params);
854 if (!channel_layout.empty())
856 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
859 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout + L" not found."));
861 config.out_channel_layout = *found_layout;
864 return spl::make_shared<decklink_consumer_proxy>(config);
867 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
868 const boost::property_tree::wptree& ptree, core::interaction_sink*)
870 configuration config;
872 auto keyer = ptree.get(L"keyer", L"default");
873 if(keyer == L"external")
874 config.keyer = configuration::keyer_t::external_keyer;
875 else if(keyer == L"internal")
876 config.keyer = configuration::keyer_t::internal_keyer;
877 else if (keyer == L"external_separate_device")
878 config.keyer = configuration::keyer_t::external_separate_device_keyer;
880 auto latency = ptree.get(L"latency", L"default");
881 if(latency == L"low")
882 config.latency = configuration::latency_t::low_latency;
883 else if(latency == L"normal")
884 config.latency = configuration::latency_t::normal_latency;
886 auto channel_layout = ptree.get_optional<std::wstring>(L"channel-layout");
890 CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
892 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
895 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
897 config.out_channel_layout = *found_layout;
900 config.key_only = ptree.get(L"key-only", config.key_only);
901 config.device_index = ptree.get(L"device", config.device_index);
902 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
903 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
904 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
906 return spl::make_shared<decklink_consumer_proxy>(config);
912 ##############################################################################
918 BMD Developer Support
919 developer@blackmagic-design.com
921 -----------------------------------------------------------------------------
923 Thanks for your inquiry. The minimum number of frames that you can preroll
924 for scheduled playback is three frames for video and four frames for audio.
925 As you mentioned if you preroll less frames then playback will not start or
926 playback will be very sporadic. From our experience with Media Express, we
927 recommended that at least seven frames are prerolled for smooth playback.
929 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
930 There can be around 3 frames worth of latency on scheduled output.
931 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
932 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
933 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
934 guarantee that the provided frame will be output as soon the previous
935 frame output has been completed.
936 ################################################################################
940 ##############################################################################
941 Async DMA Transfer without redundant copying
946 BMD Developer Support
947 developer@blackmagic-design.com
949 -----------------------------------------------------------------------------
951 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
952 and providing a pointer to your video buffer when GetBytes() is called.
953 This may help to keep copying to a minimum. Please ensure that the pixel
954 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
955 have to colourspace convert which may result in additional copying.
956 ################################################################################