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/array.h>
44 #include <common/future.h>
45 #include <common/cache_aligned_vector.h>
46 #include <common/timer.h>
47 #include <common/param.h>
49 #include <tbb/concurrent_queue.h>
51 #include <common/assert.h>
52 #include <boost/lexical_cast.hpp>
53 #include <boost/circular_buffer.hpp>
54 #include <boost/property_tree/ptree.hpp>
56 namespace caspar { namespace decklink {
64 external_separate_device_keyer,
65 default_keyer = external_keyer
72 default_latency = normal_latency
76 int key_device_idx = 0;
77 bool embedded_audio = true;
78 keyer_t keyer = keyer_t::default_keyer;
79 latency_t latency = latency_t::default_latency;
80 bool key_only = false;
81 int base_buffer_depth = 3;
82 core::audio_channel_layout out_channel_layout = core::audio_channel_layout::invalid();
84 int buffer_depth() const
86 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
89 int key_device_index() const
91 return key_device_idx == 0 ? device_index + 1 : key_device_idx;
94 core::audio_channel_layout get_adjusted_layout(const core::audio_channel_layout& in_layout) const
96 auto adjusted = out_channel_layout == core::audio_channel_layout::invalid() ? in_layout : out_channel_layout;
98 if (adjusted.num_channels == 1) // Duplicate mono-signal into both left and right.
100 adjusted.num_channels = 2;
101 adjusted.channel_order.push_back(adjusted.channel_order.at(0)); // Usually FC -> FC FC
103 else if (adjusted.num_channels == 2)
105 adjusted.num_channels = 2;
107 else if (adjusted.num_channels <= 8)
109 adjusted.num_channels = 8;
111 else // Over 8 always pad to 16 or drop >16
113 adjusted.num_channels = 16;
121 const com_iface_ptr<IDeckLinkConfiguration>& config,
122 configuration::latency_t latency,
123 const std::wstring& print)
125 if (latency == configuration::latency_t::low_latency)
127 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
128 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";
130 else if (latency == configuration::latency_t::normal_latency)
132 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
133 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";
138 const com_iface_ptr<IDeckLinkAttributes>& attributes,
139 const com_iface_ptr<IDeckLinkKeyer>& decklink_keyer,
140 configuration::keyer_t keyer,
141 const std::wstring& print)
143 if (keyer == configuration::keyer_t::internal_keyer)
146 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
147 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
148 else if (FAILED(decklink_keyer->Enable(FALSE)))
149 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
150 else if (FAILED(decklink_keyer->SetLevel(255)))
151 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
153 CASPAR_LOG(info) << print << L" Enabled internal keyer.";
155 else if (keyer == configuration::keyer_t::external_keyer)
158 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
159 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
160 else if (FAILED(decklink_keyer->Enable(TRUE)))
161 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
162 else if (FAILED(decklink_keyer->SetLevel(255)))
163 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
165 CASPAR_LOG(info) << print << L" Enabled external keyer.";
169 class decklink_frame : public IDeckLinkVideoFrame
171 tbb::atomic<int> ref_count_;
172 core::const_frame frame_;
173 const core::video_format_desc format_desc_;
175 const bool key_only_;
176 cache_aligned_vector<uint8_t> data_;
178 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
180 , format_desc_(format_desc)
181 , key_only_(key_only)
188 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
190 return E_NOINTERFACE;
193 virtual ULONG STDMETHODCALLTYPE AddRef()
198 virtual ULONG STDMETHODCALLTYPE Release()
200 if(--ref_count_ == 0)
205 // IDecklinkVideoFrame
207 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
208 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
209 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
210 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
211 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
213 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
217 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
219 data_.resize(format_desc_.size, 0);
220 *buffer = data_.data();
226 data_.resize(frame_.image_data().size());
227 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
229 *buffer = data_.data();
232 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
236 CASPAR_LOG_CURRENT_EXCEPTION();
243 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
244 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
248 const core::audio_buffer& audio_data()
250 return frame_.audio_data();
253 int64_t get_age_millis() const
255 return frame_.get_age_millis();
259 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
261 const configuration config_;
262 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
263 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
264 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
265 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
266 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
267 tbb::atomic<int64_t> current_presentation_delay_;
268 tbb::atomic<int64_t> scheduled_frames_completed_;
270 key_video_context(const configuration& config, const std::wstring& print)
273 current_presentation_delay_ = 0;
274 scheduled_frames_completed_ = 0;
276 set_latency(configuration_, config.latency, print);
277 set_keyer(attributes_, keyer_, config.keyer, print);
279 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
280 CASPAR_THROW_EXCEPTION(caspar_exception()
281 << msg_info(print + L" Failed to set key playback completion callback.")
282 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
285 template<typename Print>
286 void enable_video(BMDDisplayMode display_mode, const Print& print)
288 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
289 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable key video output."));
291 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
292 CASPAR_THROW_EXCEPTION(caspar_exception()
293 << msg_info(print() + L" Failed to set key playback completion callback.")
294 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
297 virtual ~key_video_context()
301 output_->StopScheduledPlayback(0, nullptr, 0);
302 output_->DisableVideoOutput();
306 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
307 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
308 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
310 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
315 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
316 IDeckLinkVideoFrame* completed_frame,
317 BMDOutputFrameCompletionResult result)
319 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
320 current_presentation_delay_ = dframe->get_age_millis();
321 ++scheduled_frames_completed_;
323 // Let the fill callback keep the pace, so no scheduling here.
329 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
331 const int channel_index_;
332 const configuration config_;
334 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
335 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
336 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
337 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
338 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
340 tbb::spin_mutex exception_mutex_;
341 std::exception_ptr exception_;
343 tbb::atomic<bool> is_running_;
345 const std::wstring model_name_ = get_model_name(decklink_);
346 const core::video_format_desc format_desc_;
347 const core::audio_channel_layout in_channel_layout_;
348 const core::audio_channel_layout out_channel_layout_ = config_.get_adjusted_layout(in_channel_layout_);
349 core::audio_channel_remapper channel_remapper_ { in_channel_layout_, out_channel_layout_ };
350 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
352 long long video_scheduled_ = 0;
353 long long audio_scheduled_ = 0;
355 int preroll_count_ = 0;
357 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
359 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
360 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
362 spl::shared_ptr<diagnostics::graph> graph_;
363 caspar::timer tick_timer_;
364 retry_task<bool> send_completion_;
365 reference_signal_detector reference_signal_detector_ { output_ };
366 tbb::atomic<int64_t> current_presentation_delay_;
367 tbb::atomic<int64_t> scheduled_frames_completed_;
368 std::unique_ptr<key_video_context> key_context_;
372 const configuration& config,
373 const core::video_format_desc& format_desc,
374 const core::audio_channel_layout& in_channel_layout,
376 : channel_index_(channel_index)
378 , format_desc_(format_desc)
379 , in_channel_layout_(in_channel_layout)
382 current_presentation_delay_ = 0;
383 scheduled_frames_completed_ = 0;
385 video_frame_buffer_.set_capacity(1);
387 // Blackmagic calls RenderAudioSamples() 50 times per second
388 // regardless of video mode so we sometimes need to give them
389 // samples from 2 frames in order to keep up
390 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
392 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
393 key_context_.reset(new key_video_context(config, print()));
395 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
396 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
397 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
398 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
399 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
400 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
404 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
407 graph_->set_text(print());
408 diagnostics::register_graph(graph_);
410 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
412 if(config.embedded_audio)
415 set_latency(configuration_, config.latency, print());
416 set_keyer(attributes_, keyer_, config.keyer, print());
418 if(config.embedded_audio)
419 output_->BeginAudioPreroll();
421 for(int n = 0; n < buffer_size_; ++n)
422 schedule_next_video(core::const_frame::empty());
424 if(!config.embedded_audio)
431 video_frame_buffer_.try_push(core::const_frame::empty());
432 audio_frame_buffer_.try_push(core::const_frame::empty());
434 if(output_ != nullptr)
436 output_->StopScheduledPlayback(0, nullptr, 0);
437 if(config_.embedded_audio)
438 output_->DisableAudioOutput();
439 output_->DisableVideoOutput();
445 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
446 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable audio output."));
448 if(FAILED(output_->SetAudioCallback(this)))
449 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not set audio callback."));
451 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
454 void enable_video(BMDDisplayMode display_mode)
456 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
457 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable fill video output."));
459 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
460 CASPAR_THROW_EXCEPTION(caspar_exception()
461 << msg_info(print() + L" Failed to set fill playback completion callback.")
462 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
465 key_context_->enable_video(display_mode, [this]() { return print(); });
468 void start_playback()
470 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
471 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule fill playback."));
473 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
474 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule key playback."));
477 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
478 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
479 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
481 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
484 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
488 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
495 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
496 current_presentation_delay_ = dframe->get_age_millis();
497 ++scheduled_frames_completed_;
503 scheduled_frames_completed_
504 - key_context_->scheduled_frames_completed_)
507 if(result == bmdOutputFrameDisplayedLate)
509 graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
510 video_scheduled_ += format_desc_.duration;
511 audio_scheduled_ += dframe->audio_data().size() / out_channel_layout_.num_channels;
512 //++video_scheduled_;
513 //audio_scheduled_ += format_desc_.audio_cadence[0];
514 //++audio_scheduled_;
516 else if(result == bmdOutputFrameDropped)
517 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
518 else if(result == bmdOutputFrameFlushed)
519 graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
522 output_->GetBufferedVideoFrameCount(&buffered);
523 graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
525 auto frame = core::const_frame::empty();
526 video_frame_buffer_.pop(frame);
527 send_completion_.try_completion();
528 schedule_next_video(frame);
532 lock(exception_mutex_, [&]
534 exception_ = std::current_exception();
542 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
551 if(++preroll_count_ >= buffer_size_)
553 output_->EndAudioPreroll();
558 schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
563 auto frame = core::const_frame::empty();
565 while(audio_frame_buffer_.try_pop(frame))
568 output_->GetBufferedAudioSampleFrameCount(&buffered);
569 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
571 send_completion_.try_completion();
572 schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
578 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
579 exception_ = std::current_exception();
587 void schedule_next_audio(const T& audio_data)
589 auto sample_frame_count = static_cast<int>(audio_data.size()/out_channel_layout_.num_channels);
591 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
593 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
594 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
596 audio_scheduled_ += sample_frame_count;
599 void schedule_next_video(core::const_frame frame)
603 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true));
604 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
605 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
608 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only));
609 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
610 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
612 video_scheduled_ += format_desc_.duration;
614 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
615 tick_timer_.restart();
617 reference_signal_detector_.detect_change([this]() { return print(); });
620 std::future<bool> send(core::const_frame frame)
622 auto exception = lock(exception_mutex_, [&]
627 if(exception != nullptr)
628 std::rethrow_exception(exception);
631 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Is not running."));
633 bool audio_ready = !config_.embedded_audio;
634 bool video_ready = false;
636 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
639 audio_ready = audio_frame_buffer_.try_push(frame);
642 video_ready = video_frame_buffer_.try_push(frame);
644 if (audio_ready && video_ready)
647 return boost::optional<bool>();
651 return make_ready_future(true);
653 send_completion_.set_task(enqueue_task);
655 return send_completion_.get_future();
658 std::wstring print() const
660 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
661 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
662 boost::lexical_cast<std::wstring>(config_.device_index) +
664 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
666 format_desc_.name + L"]";
668 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
669 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
673 struct decklink_consumer_proxy : public core::frame_consumer
675 core::monitor::subject monitor_subject_;
676 const configuration config_;
677 std::unique_ptr<decklink_consumer> consumer_;
678 core::video_format_desc format_desc_;
682 decklink_consumer_proxy(const configuration& config)
684 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
686 auto ctx = core::diagnostics::call_context::for_thread();
687 executor_.begin_invoke([=]
689 core::diagnostics::call_context::for_thread() = ctx;
694 ~decklink_consumer_proxy()
705 void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
707 format_desc_ = format_desc;
711 consumer_.reset(new decklink_consumer(config_, format_desc, channel_layout, channel_index));
715 std::future<bool> send(core::const_frame frame) override
717 return consumer_->send(frame);
720 std::wstring print() const override
722 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
725 std::wstring name() const override
730 boost::property_tree::wptree info() const override
732 boost::property_tree::wptree info;
733 info.add(L"type", L"decklink");
734 info.add(L"key-only", config_.key_only);
735 info.add(L"device", config_.device_index);
737 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
739 info.add(L"key-device", config_.key_device_index());
742 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
743 info.add(L"embedded-audio", config_.embedded_audio);
744 info.add(L"presentation-frame-age", presentation_frame_age_millis());
745 //info.add(L"internal-key", config_.internal_key);
749 int buffer_depth() const override
751 return config_.buffer_depth() + 2;
754 int index() const override
756 return 300 + config_.device_index;
759 int64_t presentation_frame_age_millis() const override
761 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
764 core::monitor::subject& monitor_output()
766 return monitor_subject_;
770 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
772 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
773 sink.syntax(L"DECKLINK "
774 L"{[device_index:int]|1} "
775 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
776 L"{[low_latency:LOW_LATENCY]} "
777 L"{[embedded_audio:EMBEDDED_AUDIO]} "
778 L"{[key_only:KEY_ONLY]} "
779 L"{CHANNEL_LAYOUT [channel_layout:string]}");
780 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
782 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
784 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
785 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
786 ->item(L"low_latency", L"Tries to enable low latency if given.")
787 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
789 L" will extract only the alpha channel from the "
790 L"channel. This is useful when you have two SDI video cards, and neither has native support "
791 L"for separate fill/key output")
792 ->item(L"channel_layout", L"If specified, overrides the audio channel layout used by the channel.");
793 sink.para()->text(L"Examples:");
794 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
795 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
796 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
798 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
799 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.");
801 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
802 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
805 spl::shared_ptr<core::frame_consumer> create_consumer(
806 const std::vector<std::wstring>& params, core::interaction_sink*)
808 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
809 return core::frame_consumer::empty();
811 configuration config;
813 if (params.size() > 1)
814 config.device_index = boost::lexical_cast<int>(params.at(1));
816 if (contains_param(L"INTERNAL_KEY", params))
817 config.keyer = configuration::keyer_t::internal_keyer;
818 else if (contains_param(L"EXTERNAL_KEY", params))
819 config.keyer = configuration::keyer_t::external_keyer;
820 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
821 config.keyer = configuration::keyer_t::external_separate_device_keyer;
823 config.keyer = configuration::keyer_t::default_keyer;
825 if (contains_param(L"LOW_LATENCY", params))
826 config.latency = configuration::latency_t::low_latency;
828 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
829 config.key_only = contains_param(L"KEY_ONLY", params);
831 auto channel_layout = get_param(L"CHANNEL_LAYOUT", params);
833 if (!channel_layout.empty())
835 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
838 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout + L" not found."));
840 config.out_channel_layout = *found_layout;
843 return spl::make_shared<decklink_consumer_proxy>(config);
846 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
847 const boost::property_tree::wptree& ptree, core::interaction_sink*)
849 configuration config;
851 auto keyer = ptree.get(L"keyer", L"default");
852 if(keyer == L"external")
853 config.keyer = configuration::keyer_t::external_keyer;
854 else if(keyer == L"internal")
855 config.keyer = configuration::keyer_t::internal_keyer;
856 else if (keyer == L"external_separate_device")
857 config.keyer = configuration::keyer_t::external_separate_device_keyer;
859 auto latency = ptree.get(L"latency", L"default");
860 if(latency == L"low")
861 config.latency = configuration::latency_t::low_latency;
862 else if(latency == L"normal")
863 config.latency = configuration::latency_t::normal_latency;
865 auto channel_layout = ptree.get_optional<std::wstring>(L"channel-layout");
869 CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
871 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
874 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
876 config.out_channel_layout = *found_layout;
879 config.key_only = ptree.get(L"key-only", config.key_only);
880 config.device_index = ptree.get(L"device", config.device_index);
881 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
882 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
883 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
885 return spl::make_shared<decklink_consumer_proxy>(config);
891 ##############################################################################
897 BMD Developer Support
898 developer@blackmagic-design.com
900 -----------------------------------------------------------------------------
902 Thanks for your inquiry. The minimum number of frames that you can preroll
903 for scheduled playback is three frames for video and four frames for audio.
904 As you mentioned if you preroll less frames then playback will not start or
905 playback will be very sporadic. From our experience with Media Express, we
906 recommended that at least seven frames are prerolled for smooth playback.
908 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
909 There can be around 3 frames worth of latency on scheduled output.
910 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
911 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
912 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
913 guarantee that the provided frame will be output as soon the previous
914 frame output has been completed.
915 ################################################################################
919 ##############################################################################
920 Async DMA Transfer without redundant copying
925 BMD Developer Support
926 developer@blackmagic-design.com
928 -----------------------------------------------------------------------------
930 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
931 and providing a pointer to your video buffer when GetBytes() is called.
932 This may help to keep copying to a minimum. Please ensure that the pixel
933 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
934 have to colourspace convert which may result in additional copying.
935 ################################################################################