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/mixer/audio/audio_mixer.h>
32 #include <core/consumer/frame_consumer.h>
33 #include <core/diagnostics/call_context.h>
34 #include <core/help/help_sink.h>
35 #include <core/help/help_repository.h>
37 #include <common/executor.h>
38 #include <common/lock.h>
39 #include <common/diagnostics/graph.h>
40 #include <common/except.h>
41 #include <common/memshfl.h>
42 #include <common/array.h>
43 #include <common/future.h>
44 #include <common/cache_aligned_vector.h>
45 #include <common/timer.h>
46 #include <common/param.h>
48 #include <tbb/concurrent_queue.h>
50 #include <common/assert.h>
51 #include <boost/lexical_cast.hpp>
52 #include <boost/circular_buffer.hpp>
53 #include <boost/property_tree/ptree.hpp>
55 namespace caspar { namespace decklink {
63 external_separate_device_keyer,
75 int key_device_idx = 0;
76 bool embedded_audio = true;
77 keyer_t keyer = keyer_t::default_keyer;
78 latency_t latency = latency_t::default_latency;
79 bool key_only = false;
80 int base_buffer_depth = 3;
82 int buffer_depth() const
84 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
87 int key_device_index() const
89 return key_device_idx == 0 ? device_index + 1 : key_device_idx;
94 const com_iface_ptr<IDeckLinkConfiguration>& config,
95 configuration::latency_t latency,
96 const std::wstring& print)
98 if (latency == configuration::latency_t::low_latency)
100 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
101 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";
103 else if (latency == configuration::latency_t::normal_latency)
105 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
106 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";
111 const com_iface_ptr<IDeckLinkAttributes>& attributes,
112 const com_iface_ptr<IDeckLinkKeyer>& decklink_keyer,
113 configuration::keyer_t keyer,
114 const std::wstring& print)
116 if (keyer == configuration::keyer_t::internal_keyer
117 || keyer == configuration::keyer_t::external_separate_device_keyer)
120 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
121 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
122 else if (FAILED(decklink_keyer->Enable(FALSE)))
123 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
124 else if (FAILED(decklink_keyer->SetLevel(255)))
125 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
127 CASPAR_LOG(info) << print << L" Enabled internal keyer.";
129 else if (keyer == configuration::keyer_t::external_keyer)
132 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
133 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
134 else if (FAILED(decklink_keyer->Enable(TRUE)))
135 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
136 else if (FAILED(decklink_keyer->SetLevel(255)))
137 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
139 CASPAR_LOG(info) << print << L" Enabled external keyer.";
143 class decklink_frame : public IDeckLinkVideoFrame
145 tbb::atomic<int> ref_count_;
146 core::const_frame frame_;
147 const core::video_format_desc format_desc_;
149 const bool key_only_;
150 cache_aligned_vector<uint8_t> data_;
152 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
154 , format_desc_(format_desc)
155 , key_only_(key_only)
162 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
164 return E_NOINTERFACE;
167 virtual ULONG STDMETHODCALLTYPE AddRef()
172 virtual ULONG STDMETHODCALLTYPE Release()
174 if(--ref_count_ == 0)
179 // IDecklinkVideoFrame
181 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
182 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
183 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
184 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
185 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
187 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
191 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
193 data_.resize(format_desc_.size, 0);
194 *buffer = data_.data();
200 data_.resize(frame_.image_data().size());
201 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
203 *buffer = data_.data();
206 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
210 CASPAR_LOG_CURRENT_EXCEPTION();
217 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
218 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
222 const core::audio_buffer& audio_data()
224 return frame_.audio_data();
227 int64_t get_age_millis() const
229 return frame_.get_age_millis();
233 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
235 const configuration config_;
236 com_ptr<IDeckLink> decklink_ = get_device(config_.key_device_index());
237 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
238 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
239 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
240 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
241 tbb::atomic<int64_t> current_presentation_delay_;
242 tbb::atomic<int64_t> scheduled_frames_completed_;
244 key_video_context(const configuration& config, const std::wstring& print)
247 current_presentation_delay_ = 0;
248 scheduled_frames_completed_ = 0;
250 set_latency(configuration_, config.latency, print);
251 set_keyer(attributes_, keyer_, config.keyer, print);
253 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
254 CASPAR_THROW_EXCEPTION(caspar_exception()
255 << msg_info(u8(print) + " Failed to set key playback completion callback.")
256 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
259 template<typename Print>
260 void enable_video(BMDDisplayMode display_mode, const Print& print)
262 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
263 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable key video output."));
265 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
266 CASPAR_THROW_EXCEPTION(caspar_exception()
267 << msg_info(u8(print()) + " Failed to set key playback completion callback.")
268 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
271 virtual ~key_video_context()
275 output_->StopScheduledPlayback(0, nullptr, 0);
276 output_->DisableVideoOutput();
280 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
281 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
282 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
284 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
289 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
290 IDeckLinkVideoFrame* completed_frame,
291 BMDOutputFrameCompletionResult result)
293 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
294 current_presentation_delay_ = dframe->get_age_millis();
295 ++scheduled_frames_completed_;
297 // Let the fill callback keep the pace, so no scheduling here.
303 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
305 const int channel_index_;
306 const configuration config_;
308 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
309 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
310 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
311 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
312 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
314 tbb::spin_mutex exception_mutex_;
315 std::exception_ptr exception_;
317 tbb::atomic<bool> is_running_;
319 const std::wstring model_name_ = get_model_name(decklink_);
320 const core::video_format_desc format_desc_;
321 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
323 long long video_scheduled_ = 0;
324 long long audio_scheduled_ = 0;
326 int preroll_count_ = 0;
328 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
330 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
331 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
333 spl::shared_ptr<diagnostics::graph> graph_;
334 tbb::atomic<int64_t> current_presentation_delay_;
335 caspar::timer tick_timer_;
336 retry_task<bool> send_completion_;
337 tbb::atomic<int64_t> scheduled_frames_completed_;
338 std::unique_ptr<key_video_context> key_context_;
341 decklink_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index)
342 : channel_index_(channel_index)
344 , format_desc_(format_desc)
347 current_presentation_delay_ = 0;
348 scheduled_frames_completed_ = 0;
350 video_frame_buffer_.set_capacity(1);
352 // Blackmagic calls RenderAudioSamples() 50 times per second
353 // regardless of video mode so we sometimes need to give them
354 // samples from 2 frames in order to keep up
355 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
357 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
358 key_context_.reset(new key_video_context(config, print()));
360 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
361 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
362 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
363 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
364 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
365 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
369 graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
372 graph_->set_text(print());
373 diagnostics::register_graph(graph_);
375 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
377 if(config.embedded_audio)
380 set_latency(configuration_, config.latency, print());
381 set_keyer(attributes_, keyer_, config.keyer, print());
383 if(config.embedded_audio)
384 output_->BeginAudioPreroll();
386 for(int n = 0; n < buffer_size_; ++n)
387 schedule_next_video(core::const_frame::empty());
389 if(!config.embedded_audio)
396 video_frame_buffer_.try_push(core::const_frame::empty());
397 audio_frame_buffer_.try_push(core::const_frame::empty());
399 if(output_ != nullptr)
401 output_->StopScheduledPlayback(0, nullptr, 0);
402 if(config_.embedded_audio)
403 output_->DisableAudioOutput();
404 output_->DisableVideoOutput();
410 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))
411 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable audio output."));
413 if(FAILED(output_->SetAudioCallback(this)))
414 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not set audio callback."));
416 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
419 void enable_video(BMDDisplayMode display_mode)
421 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
422 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable fill video output."));
424 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
425 CASPAR_THROW_EXCEPTION(caspar_exception()
426 << msg_info(u8(print()) + " Failed to set fill playback completion callback.")
427 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
430 key_context_->enable_video(display_mode, [this]() { return print(); });
433 void start_playback()
435 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
436 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule fill playback."));
438 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
439 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule key playback."));
442 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
443 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
444 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
446 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
449 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
453 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
460 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
461 current_presentation_delay_ = dframe->get_age_millis();
462 ++scheduled_frames_completed_;
468 scheduled_frames_completed_
469 - key_context_->scheduled_frames_completed_)
472 if(result == bmdOutputFrameDisplayedLate)
474 graph_->set_tag("late-frame");
475 video_scheduled_ += format_desc_.duration;
476 audio_scheduled_ += dframe->audio_data().size() / format_desc_.audio_channels;
477 //++video_scheduled_;
478 //audio_scheduled_ += format_desc_.audio_cadence[0];
479 //++audio_scheduled_;
481 else if(result == bmdOutputFrameDropped)
482 graph_->set_tag("dropped-frame");
483 else if(result == bmdOutputFrameFlushed)
484 graph_->set_tag("flushed-frame");
486 auto frame = core::const_frame::empty();
487 video_frame_buffer_.pop(frame);
488 send_completion_.try_completion();
489 schedule_next_video(frame);
492 output_->GetBufferedVideoFrameCount(&buffered);
493 graph_->set_value("buffered-video", static_cast<double>(buffered)/format_desc_.fps);
497 lock(exception_mutex_, [&]
499 exception_ = std::current_exception();
507 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
516 if(++preroll_count_ >= buffer_size_)
518 output_->EndAudioPreroll();
523 schedule_next_audio(core::audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * format_desc_.audio_channels, 0));
528 auto frame = core::const_frame::empty();
530 while(audio_frame_buffer_.try_pop(frame))
532 send_completion_.try_completion();
533 schedule_next_audio(frame.audio_data());
538 output_->GetBufferedAudioSampleFrameCount(&buffered);
539 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * format_desc_.audio_channels * 2));
543 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
544 exception_ = std::current_exception();
552 void schedule_next_audio(const T& audio_data)
554 auto sample_frame_count = static_cast<int>(audio_data.size()/format_desc_.audio_channels);
556 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
558 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
559 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
561 audio_scheduled_ += sample_frame_count;
564 void schedule_next_video(core::const_frame frame)
568 auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true));
569 if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
570 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
573 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only));
574 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
575 CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
577 video_scheduled_ += format_desc_.duration;
579 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
580 tick_timer_.restart();
583 std::future<bool> send(core::const_frame frame)
585 auto exception = lock(exception_mutex_, [&]
590 if(exception != nullptr)
591 std::rethrow_exception(exception);
594 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Is not running."));
596 bool audio_ready = !config_.embedded_audio;
597 bool video_ready = false;
599 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
602 audio_ready = audio_frame_buffer_.try_push(frame);
605 video_ready = video_frame_buffer_.try_push(frame);
607 if (audio_ready && video_ready)
610 return boost::optional<bool>();
614 return make_ready_future(true);
616 send_completion_.set_task(enqueue_task);
618 return send_completion_.get_future();
621 std::wstring print() const
623 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
624 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
625 boost::lexical_cast<std::wstring>(config_.device_index) +
627 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
629 format_desc_.name + L"]";
631 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
632 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
636 struct decklink_consumer_proxy : public core::frame_consumer
638 core::monitor::subject monitor_subject_;
639 const configuration config_;
640 std::unique_ptr<decklink_consumer> consumer_;
641 core::video_format_desc format_desc_;
645 decklink_consumer_proxy(const configuration& config)
647 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
649 auto ctx = core::diagnostics::call_context::for_thread();
650 executor_.begin_invoke([=]
652 core::diagnostics::call_context::for_thread() = ctx;
657 ~decklink_consumer_proxy()
668 void initialize(const core::video_format_desc& format_desc, int channel_index) override
670 format_desc_ = format_desc;
674 consumer_.reset(new decklink_consumer(config_, format_desc, channel_index));
678 std::future<bool> send(core::const_frame frame) override
680 return consumer_->send(frame);
683 std::wstring print() const override
685 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
688 std::wstring name() const override
693 boost::property_tree::wptree info() const override
695 boost::property_tree::wptree info;
696 info.add(L"type", L"decklink");
697 info.add(L"key-only", config_.key_only);
698 info.add(L"device", config_.device_index);
700 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
702 info.add(L"key-device", config_.key_device_index());
705 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
706 info.add(L"embedded-audio", config_.embedded_audio);
707 info.add(L"presentation-frame-age", presentation_frame_age_millis());
708 //info.add(L"internal-key", config_.internal_key);
712 int buffer_depth() const override
714 return config_.buffer_depth() + 2;
717 int index() const override
719 return 300 + config_.device_index;
722 int64_t presentation_frame_age_millis() const override
724 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
727 core::monitor::subject& monitor_output()
729 return monitor_subject_;
733 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
735 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
736 sink.syntax(L"DECKLINK "
737 L"{[device_index:int]|1} "
738 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
739 L"{[low_latency:LOW_LATENCY]} "
740 L"{[embedded_audio:EMBEDDED_AUDIO]} "
741 L"{[key_only:KEY_ONLY]}");
742 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
744 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
746 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
747 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
748 ->item(L"low_latency", L"Tries to enable low latency if given.")
749 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
751 L" will extract only the alpha channel from the "
752 L"channel. This is useful when you have two SDI video cards, and neither has native support "
753 L"for separate fill/key output");
754 sink.para()->text(L"Examples:");
755 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
756 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
757 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
759 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
760 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.");
762 L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
763 L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
766 spl::shared_ptr<core::frame_consumer> create_consumer(
767 const std::vector<std::wstring>& params, core::interaction_sink*)
769 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
770 return core::frame_consumer::empty();
772 configuration config;
774 if (params.size() > 1)
775 config.device_index = boost::lexical_cast<int>(params.at(1));
777 if (contains_param(L"INTERNAL_KEY", params))
778 config.keyer = configuration::keyer_t::internal_keyer;
779 else if (contains_param(L"EXTERNAL_KEY", params))
780 config.keyer = configuration::keyer_t::external_keyer;
781 else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
782 config.keyer = configuration::keyer_t::external_separate_device_keyer;
784 config.keyer = configuration::keyer_t::default_keyer;
786 if (contains_param(L"LOW_LATENCY", params))
787 config.latency = configuration::latency_t::low_latency;
789 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
790 config.key_only = contains_param(L"KEY_ONLY", params);
792 return spl::make_shared<decklink_consumer_proxy>(config);
795 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
796 const boost::property_tree::wptree& ptree, core::interaction_sink*)
798 configuration config;
800 auto keyer = ptree.get(L"keyer", L"default");
801 if(keyer == L"external")
802 config.keyer = configuration::keyer_t::external_keyer;
803 else if(keyer == L"internal")
804 config.keyer = configuration::keyer_t::internal_keyer;
805 else if (keyer == L"external_separate_device")
806 config.keyer = configuration::keyer_t::external_separate_device_keyer;
808 auto latency = ptree.get(L"latency", L"normal");
809 if(latency == L"low")
810 config.latency = configuration::latency_t::low_latency;
811 else if(latency == L"normal")
812 config.latency = configuration::latency_t::normal_latency;
814 config.key_only = ptree.get(L"key-only", config.key_only);
815 config.device_index = ptree.get(L"device", config.device_index);
816 config.key_device_idx = ptree.get(L"key-device", config.key_device_idx);
817 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
818 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
820 return spl::make_shared<decklink_consumer_proxy>(config);
826 ##############################################################################
832 BMD Developer Support
833 developer@blackmagic-design.com
835 -----------------------------------------------------------------------------
837 Thanks for your inquiry. The minimum number of frames that you can preroll
838 for scheduled playback is three frames for video and four frames for audio.
839 As you mentioned if you preroll less frames then playback will not start or
840 playback will be very sporadic. From our experience with Media Express, we
841 recommended that at least seven frames are prerolled for smooth playback.
843 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
844 There can be around 3 frames worth of latency on scheduled output.
845 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
846 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
847 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
848 guarantee that the provided frame will be output as soon the previous
849 frame output has been completed.
850 ################################################################################
854 ##############################################################################
855 Async DMA Transfer without redundant copying
860 BMD Developer Support
861 developer@blackmagic-design.com
863 -----------------------------------------------------------------------------
865 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
866 and providing a pointer to your video buffer when GetBytes() is called.
867 This may help to keep copying to a minimum. Please ensure that the pixel
868 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
869 have to colourspace convert which may result in additional copying.
870 ################################################################################