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 {
74 bool embedded_audio = true;
75 keyer_t keyer = keyer_t::default_keyer;
76 latency_t latency = latency_t::default_latency;
77 bool key_only = false;
78 int base_buffer_depth = 3;
80 int buffer_depth() const
82 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
86 class decklink_frame : public IDeckLinkVideoFrame
88 tbb::atomic<int> ref_count_;
89 core::const_frame frame_;
90 const core::video_format_desc format_desc_;
93 cache_aligned_vector<uint8_t> data_;
95 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
97 , format_desc_(format_desc)
105 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
107 return E_NOINTERFACE;
110 virtual ULONG STDMETHODCALLTYPE AddRef()
115 virtual ULONG STDMETHODCALLTYPE Release()
117 if(--ref_count_ == 0)
122 // IDecklinkVideoFrame
124 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
125 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
126 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
127 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
128 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
130 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
134 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
136 data_.resize(format_desc_.size, 0);
137 *buffer = data_.data();
143 data_.resize(frame_.image_data().size());
144 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
146 *buffer = data_.data();
149 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
153 CASPAR_LOG_CURRENT_EXCEPTION();
160 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
161 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
165 const core::audio_buffer& audio_data()
167 return frame_.audio_data();
170 int64_t get_age_millis() const
172 return frame_.get_age_millis();
176 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
178 const int channel_index_;
179 const configuration config_;
181 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
182 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
183 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
184 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
185 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
187 tbb::spin_mutex exception_mutex_;
188 std::exception_ptr exception_;
190 tbb::atomic<bool> is_running_;
192 const std::wstring model_name_ = get_model_name(decklink_);
193 const core::video_format_desc format_desc_;
194 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
196 long long video_scheduled_ = 0;
197 long long audio_scheduled_ = 0;
199 int preroll_count_ = 0;
201 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
203 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
204 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
206 spl::shared_ptr<diagnostics::graph> graph_;
207 tbb::atomic<int64_t> current_presentation_delay_;
208 caspar::timer tick_timer_;
209 retry_task<bool> send_completion_;
212 decklink_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index)
213 : channel_index_(channel_index)
215 , format_desc_(format_desc)
218 current_presentation_delay_ = 0;
220 video_frame_buffer_.set_capacity(1);
222 // Blackmagic calls RenderAudioSamples() 50 times per second
223 // regardless of video mode so we sometimes need to give them
224 // samples from 2 frames in order to keep up
225 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
227 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
228 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
229 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
230 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
231 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
232 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
233 graph_->set_text(print());
234 diagnostics::register_graph(graph_);
236 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
238 if(config.embedded_audio)
241 set_latency(config.latency);
242 set_keyer(config.keyer);
244 if(config.embedded_audio)
245 output_->BeginAudioPreroll();
247 for(int n = 0; n < buffer_size_; ++n)
248 schedule_next_video(core::const_frame::empty());
250 if(!config.embedded_audio)
257 video_frame_buffer_.try_push(core::const_frame::empty());
258 audio_frame_buffer_.try_push(core::const_frame::empty());
260 if(output_ != nullptr)
262 output_->StopScheduledPlayback(0, nullptr, 0);
263 if(config_.embedded_audio)
264 output_->DisableAudioOutput();
265 output_->DisableVideoOutput();
269 void set_latency(configuration::latency_t latency)
271 if(latency == configuration::latency_t::low_latency)
273 configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
274 CASPAR_LOG(info) << print() << L" Enabled low-latency mode.";
276 else if(latency == configuration::latency_t::normal_latency)
278 configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
279 CASPAR_LOG(info) << print() << L" Disabled low-latency mode.";
283 void set_keyer(configuration::keyer_t keyer)
285 if(keyer == configuration::keyer_t::internal_keyer)
288 if(SUCCEEDED(attributes_->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
289 CASPAR_LOG(error) << print() << L" Failed to enable internal keyer.";
290 else if(FAILED(keyer_->Enable(FALSE)))
291 CASPAR_LOG(error) << print() << L" Failed to enable internal keyer.";
292 else if(FAILED(keyer_->SetLevel(255)))
293 CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";
295 CASPAR_LOG(info) << print() << L" Enabled internal keyer.";
297 else if(keyer == configuration::keyer_t::external_keyer)
300 if(SUCCEEDED(attributes_->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
301 CASPAR_LOG(error) << print() << L" Failed to enable external keyer.";
302 else if(FAILED(keyer_->Enable(TRUE)))
303 CASPAR_LOG(error) << print() << L" Failed to enable external keyer.";
304 else if(FAILED(keyer_->SetLevel(255)))
305 CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";
307 CASPAR_LOG(info) << print() << L" Enabled external keyer.";
313 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))
314 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable audio output."));
316 if(FAILED(output_->SetAudioCallback(this)))
317 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not set audio callback."));
319 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
322 void enable_video(BMDDisplayMode display_mode)
324 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
325 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable video output."));
327 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
328 CASPAR_THROW_EXCEPTION(caspar_exception()
329 << msg_info(u8(print()) + " Failed to set playback completion callback.")
330 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
333 void start_playback()
335 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
336 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule playback."));
339 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
340 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
341 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
343 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
346 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
350 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
357 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
358 current_presentation_delay_ = dframe->get_age_millis();
359 if(result == bmdOutputFrameDisplayedLate)
361 graph_->set_tag("late-frame");
362 video_scheduled_ += format_desc_.duration;
363 audio_scheduled_ += dframe->audio_data().size() / format_desc_.audio_channels;
364 //++video_scheduled_;
365 //audio_scheduled_ += format_desc_.audio_cadence[0];
366 //++audio_scheduled_;
368 else if(result == bmdOutputFrameDropped)
369 graph_->set_tag("dropped-frame");
370 else if(result == bmdOutputFrameFlushed)
371 graph_->set_tag("flushed-frame");
373 auto frame = core::const_frame::empty();
374 video_frame_buffer_.pop(frame);
375 send_completion_.try_completion();
376 schedule_next_video(frame);
379 output_->GetBufferedVideoFrameCount(&buffered);
380 graph_->set_value("buffered-video", static_cast<double>(buffered)/format_desc_.fps);
384 lock(exception_mutex_, [&]
386 exception_ = std::current_exception();
394 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
403 if(++preroll_count_ >= buffer_size_)
405 output_->EndAudioPreroll();
410 schedule_next_audio(core::audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * format_desc_.audio_channels, 0));
415 auto frame = core::const_frame::empty();
417 while(audio_frame_buffer_.try_pop(frame))
419 send_completion_.try_completion();
420 schedule_next_audio(frame.audio_data());
425 output_->GetBufferedAudioSampleFrameCount(&buffered);
426 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * format_desc_.audio_channels * 2));
430 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
431 exception_ = std::current_exception();
439 void schedule_next_audio(const T& audio_data)
441 auto sample_frame_count = static_cast<int>(audio_data.size()/format_desc_.audio_channels);
443 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
445 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
446 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
448 audio_scheduled_ += sample_frame_count;
451 void schedule_next_video(core::const_frame frame)
453 auto frame2 = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only));
454 if(FAILED(output_->ScheduleVideoFrame(get_raw(frame2), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
455 CASPAR_LOG(error) << print() << L" Failed to schedule video.";
457 video_scheduled_ += format_desc_.duration;
459 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
460 tick_timer_.restart();
463 std::future<bool> send(core::const_frame frame)
465 auto exception = lock(exception_mutex_, [&]
470 if(exception != nullptr)
471 std::rethrow_exception(exception);
474 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Is not running."));
476 bool audio_ready = !config_.embedded_audio;
477 bool video_ready = false;
479 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
482 audio_ready = audio_frame_buffer_.try_push(frame);
485 video_ready = video_frame_buffer_.try_push(frame);
487 if (audio_ready && video_ready)
490 return boost::optional<bool>();
494 return make_ready_future(true);
496 send_completion_.set_task(enqueue_task);
498 return send_completion_.get_future();
501 std::wstring print() const
503 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_) + L"-" +
504 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
508 struct decklink_consumer_proxy : public core::frame_consumer
510 core::monitor::subject monitor_subject_;
511 const configuration config_;
512 std::unique_ptr<decklink_consumer> consumer_;
513 core::video_format_desc format_desc_;
517 decklink_consumer_proxy(const configuration& config)
519 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
521 auto ctx = core::diagnostics::call_context::for_thread();
522 executor_.begin_invoke([=]
524 core::diagnostics::call_context::for_thread() = ctx;
529 ~decklink_consumer_proxy()
540 void initialize(const core::video_format_desc& format_desc, int channel_index) override
542 format_desc_ = format_desc;
546 consumer_.reset(new decklink_consumer(config_, format_desc, channel_index));
550 std::future<bool> send(core::const_frame frame) override
552 return consumer_->send(frame);
555 std::wstring print() const override
557 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
560 std::wstring name() const override
565 boost::property_tree::wptree info() const override
567 boost::property_tree::wptree info;
568 info.add(L"type", L"decklink");
569 info.add(L"key-only", config_.key_only);
570 info.add(L"device", config_.device_index);
571 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
572 info.add(L"embedded-audio", config_.embedded_audio);
573 info.add(L"presentation-frame-age", presentation_frame_age_millis());
574 //info.add(L"internal-key", config_.internal_key);
578 int buffer_depth() const override
580 return config_.buffer_depth() + 2;
583 int index() const override
585 return 300 + config_.device_index;
588 int64_t presentation_frame_age_millis() const override
590 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
593 core::monitor::subject& monitor_output()
595 return monitor_subject_;
599 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
601 sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
602 sink.syntax(L"DECKLINK "
603 L"{[device_index:int]|1} "
604 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY]} "
605 L"{[low_latency:LOW_LATENCY]} "
606 L"{[embedded_audio:EMBEDDED_AUDIO]} "
607 L"{[key_only:KEY_ONLY]}");
608 sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
610 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
611 ->item(L"keyer", L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this.")
612 ->item(L"low_latency", L"Tries to enable low latency if given.")
613 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
615 L" will extract only the alpha channel from the "
616 L"channel. This is useful when you have two SDI video cards, and neither has native support "
617 L"for separate fill/key output");
618 sink.para()->text(L"Examples:");
619 sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
620 sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
621 sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
623 L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
624 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.");
627 spl::shared_ptr<core::frame_consumer> create_consumer(
628 const std::vector<std::wstring>& params, core::interaction_sink*)
630 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
631 return core::frame_consumer::empty();
633 configuration config;
635 if (params.size() > 1)
636 config.device_index = boost::lexical_cast<int>(params.at(1));
638 if (contains_param(L"INTERNAL_KEY", params))
639 config.keyer = configuration::keyer_t::internal_keyer;
640 else if (contains_param(L"EXTERNAL_KEY", params))
641 config.keyer = configuration::keyer_t::external_keyer;
643 config.keyer = configuration::keyer_t::default_keyer;
645 if (contains_param(L"LOW_LATENCY", params))
646 config.latency = configuration::latency_t::low_latency;
648 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
649 config.key_only = contains_param(L"KEY_ONLY", params);
651 return spl::make_shared<decklink_consumer_proxy>(config);
654 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
655 const boost::property_tree::wptree& ptree, core::interaction_sink*)
657 configuration config;
659 auto keyer = ptree.get(L"keyer", L"default");
660 if(keyer == L"external")
661 config.keyer = configuration::keyer_t::external_keyer;
662 else if(keyer == L"internal")
663 config.keyer = configuration::keyer_t::internal_keyer;
665 auto latency = ptree.get(L"latency", L"normal");
666 if(latency == L"low")
667 config.latency = configuration::latency_t::low_latency;
668 else if(latency == L"normal")
669 config.latency = configuration::latency_t::normal_latency;
671 config.key_only = ptree.get(L"key-only", config.key_only);
672 config.device_index = ptree.get(L"device", config.device_index);
673 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
674 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
676 return spl::make_shared<decklink_consumer_proxy>(config);
682 ##############################################################################
688 BMD Developer Support
689 developer@blackmagic-design.com
691 -----------------------------------------------------------------------------
693 Thanks for your inquiry. The minimum number of frames that you can preroll
694 for scheduled playback is three frames for video and four frames for audio.
695 As you mentioned if you preroll less frames then playback will not start or
696 playback will be very sporadic. From our experience with Media Express, we
697 recommended that at least seven frames are prerolled for smooth playback.
699 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
700 There can be around 3 frames worth of latency on scheduled output.
701 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
702 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
703 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
704 guarantee that the provided frame will be output as soon the previous
705 frame output has been completed.
706 ################################################################################
710 ##############################################################################
711 Async DMA Transfer without redundant copying
716 BMD Developer Support
717 developer@blackmagic-design.com
719 -----------------------------------------------------------------------------
721 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
722 and providing a pointer to your video buffer when GetBytes() is called.
723 This may help to keep copying to a minimum. Please ensure that the pixel
724 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
725 have to colourspace convert which may result in additional copying.
726 ################################################################################