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 "../interop/DeckLinkAPI_h.h"
30 #include <core/frame/frame.h>
31 #include <core/mixer/audio/audio_mixer.h>
33 #include <common/executor.h>
34 #include <common/lock.h>
35 #include <common/diagnostics/graph.h>
36 #include <common/except.h>
37 #include <common/memshfl.h>
38 #include <common/array.h>
39 #include <common/future.h>
41 #include <core/consumer/frame_consumer.h>
43 #include <tbb/concurrent_queue.h>
44 #include <tbb/cache_aligned_allocator.h>
46 #include <common/assert.h>
47 #include <boost/lexical_cast.hpp>
48 #include <boost/circular_buffer.hpp>
49 #include <boost/timer.hpp>
50 #include <boost/property_tree/ptree.hpp>
52 namespace caspar { namespace decklink {
71 bool embedded_audio = true;
72 keyer_t keyer = keyer_t::default_keyer;
73 latency_t latency = latency_t::default_latency;
74 bool key_only = false;
75 int base_buffer_depth = 3;
77 int buffer_depth() const
79 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
83 class decklink_frame : public IDeckLinkVideoFrame
85 tbb::atomic<int> ref_count_;
86 core::const_frame frame_;
87 const core::video_format_desc format_desc_;
90 std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> data_;
92 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
94 , format_desc_(format_desc)
102 STDMETHOD (QueryInterface(REFIID, LPVOID*))
104 return E_NOINTERFACE;
107 STDMETHOD_(ULONG, AddRef())
112 STDMETHOD_(ULONG, Release())
114 if(--ref_count_ == 0)
119 // IDecklinkVideoFrame
121 STDMETHOD_(long, GetWidth()) {return static_cast<long>(format_desc_.width);}
122 STDMETHOD_(long, GetHeight()) {return static_cast<long>(format_desc_.height);}
123 STDMETHOD_(long, GetRowBytes()) {return static_cast<long>(format_desc_.width*4);}
124 STDMETHOD_(BMDPixelFormat, GetPixelFormat()) {return bmdFormat8BitBGRA;}
125 STDMETHOD_(BMDFrameFlags, GetFlags()) {return bmdFrameFlagDefault;}
127 STDMETHOD(GetBytes(void** buffer))
131 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
133 data_.resize(format_desc_.size, 0);
134 *buffer = data_.data();
140 data_.resize(frame_.image_data().size());
141 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
143 *buffer = data_.data();
146 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
150 CASPAR_LOG_CURRENT_EXCEPTION();
157 STDMETHOD(GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode)){return S_FALSE;}
158 STDMETHOD(GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary)) {return S_FALSE;}
162 const core::audio_buffer& audio_data()
164 return frame_.audio_data();
168 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
170 const int channel_index_;
171 const configuration config_;
173 CComPtr<IDeckLink> decklink_ = get_device(config_.device_index);
174 CComQIPtr<IDeckLinkOutput> output_ = decklink_;
175 CComQIPtr<IDeckLinkConfiguration> configuration_ = decklink_;
176 CComQIPtr<IDeckLinkKeyer> keyer_ = decklink_;
177 CComQIPtr<IDeckLinkAttributes> attributes_ = decklink_;
179 tbb::spin_mutex exception_mutex_;
180 std::exception_ptr exception_;
182 tbb::atomic<bool> is_running_;
184 const std::wstring model_name_ = get_model_name(decklink_);
185 const core::video_format_desc format_desc_;
186 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
188 long long video_scheduled_ = 0;
189 long long audio_scheduled_ = 0;
191 int preroll_count_ = 0;
193 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
195 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
196 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
198 spl::shared_ptr<diagnostics::graph> graph_;
199 boost::timer tick_timer_;
200 retry_task<bool> send_completion_;
203 decklink_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index)
204 : channel_index_(channel_index)
206 , format_desc_(format_desc)
210 video_frame_buffer_.set_capacity(1);
212 // Blackmagic calls RenderAudioSamples() 50 times per second
213 // regardless of video mode so we sometimes need to give them
214 // samples from 2 frames in order to keep up
215 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
217 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
218 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
219 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
220 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
221 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
222 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
223 graph_->set_text(print());
224 diagnostics::register_graph(graph_);
226 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
228 if(config.embedded_audio)
231 set_latency(config.latency);
232 set_keyer(config.keyer);
234 if(config.embedded_audio)
235 output_->BeginAudioPreroll();
237 for(int n = 0; n < buffer_size_; ++n)
238 schedule_next_video(core::const_frame::empty());
240 if(!config.embedded_audio)
247 video_frame_buffer_.try_push(core::const_frame::empty());
248 audio_frame_buffer_.try_push(core::const_frame::empty());
250 if(output_ != nullptr)
252 output_->StopScheduledPlayback(0, nullptr, 0);
253 if(config_.embedded_audio)
254 output_->DisableAudioOutput();
255 output_->DisableVideoOutput();
259 void set_latency(configuration::latency_t latency)
261 if(latency == configuration::latency_t::low_latency)
263 configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
264 CASPAR_LOG(info) << print() << L" Enabled low-latency mode.";
266 else if(latency == configuration::latency_t::normal_latency)
268 configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
269 CASPAR_LOG(info) << print() << L" Disabled low-latency mode.";
273 void set_keyer(configuration::keyer_t keyer)
275 if(keyer == configuration::keyer_t::internal_keyer)
278 if(SUCCEEDED(attributes_->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
279 CASPAR_LOG(error) << print() << L" Failed to enable internal keyer.";
280 else if(FAILED(keyer_->Enable(FALSE)))
281 CASPAR_LOG(error) << print() << L" Failed to enable internal keyer.";
282 else if(FAILED(keyer_->SetLevel(255)))
283 CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";
285 CASPAR_LOG(info) << print() << L" Enabled internal keyer.";
287 else if(keyer == configuration::keyer_t::external_keyer)
290 if(SUCCEEDED(attributes_->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
291 CASPAR_LOG(error) << print() << L" Failed to enable external keyer.";
292 else if(FAILED(keyer_->Enable(TRUE)))
293 CASPAR_LOG(error) << print() << L" Failed to enable external keyer.";
294 else if(FAILED(keyer_->SetLevel(255)))
295 CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";
297 CASPAR_LOG(info) << print() << L" Enabled external keyer.";
303 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))
304 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable audio output."));
306 if(FAILED(output_->SetAudioCallback(this)))
307 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not set audio callback."));
309 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
312 void enable_video(BMDDisplayMode display_mode)
314 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
315 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable video output."));
317 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
318 CASPAR_THROW_EXCEPTION(caspar_exception()
319 << msg_info(u8(print()) + " Failed to set playback completion callback.")
320 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
323 void start_playback()
325 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
326 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule playback."));
329 STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;}
330 STDMETHOD_(ULONG, AddRef()) {return 1;}
331 STDMETHOD_(ULONG, Release()) {return 1;}
333 STDMETHOD(ScheduledPlaybackHasStopped())
336 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
340 STDMETHOD(ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result))
347 if(result == bmdOutputFrameDisplayedLate)
349 graph_->set_tag("late-frame");
350 video_scheduled_ += format_desc_.duration;
351 audio_scheduled_ += reinterpret_cast<decklink_frame*>(completed_frame)->audio_data().size()/format_desc_.audio_channels;
352 //++video_scheduled_;
353 //audio_scheduled_ += format_desc_.audio_cadence[0];
354 //++audio_scheduled_;
356 else if(result == bmdOutputFrameDropped)
357 graph_->set_tag("dropped-frame");
358 else if(result == bmdOutputFrameFlushed)
359 graph_->set_tag("flushed-frame");
361 auto frame = core::const_frame::empty();
362 video_frame_buffer_.pop(frame);
363 send_completion_.try_completion();
364 schedule_next_video(frame);
366 unsigned long buffered;
367 output_->GetBufferedVideoFrameCount(&buffered);
368 graph_->set_value("buffered-video", static_cast<double>(buffered)/format_desc_.fps);
372 lock(exception_mutex_, [&]
374 exception_ = std::current_exception();
382 STDMETHOD(RenderAudioSamples(BOOL preroll))
391 if(++preroll_count_ >= buffer_size_)
393 output_->EndAudioPreroll();
398 schedule_next_audio(core::audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * format_desc_.audio_channels, 0));
403 auto frame = core::const_frame::empty();
405 while(audio_frame_buffer_.try_pop(frame))
407 send_completion_.try_completion();
408 schedule_next_audio(frame.audio_data());
412 unsigned long buffered;
413 output_->GetBufferedAudioSampleFrameCount(&buffered);
414 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * format_desc_.audio_channels * 2));
418 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
419 exception_ = std::current_exception();
427 void schedule_next_audio(const T& audio_data)
429 auto sample_frame_count = static_cast<int>(audio_data.size()/format_desc_.audio_channels);
431 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
433 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
434 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
436 audio_scheduled_ += sample_frame_count;
439 void schedule_next_video(core::const_frame frame)
441 CComPtr<IDeckLinkVideoFrame> frame2(new decklink_frame(frame, format_desc_, config_.key_only));
442 if(FAILED(output_->ScheduleVideoFrame(frame2, video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
443 CASPAR_LOG(error) << print() << L" Failed to schedule video.";
445 video_scheduled_ += format_desc_.duration;
447 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
448 tick_timer_.restart();
451 std::future<bool> send(core::const_frame frame)
453 auto exception = lock(exception_mutex_, [&]
458 if(exception != nullptr)
459 std::rethrow_exception(exception);
462 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Is not running."));
464 bool audio_ready = !config_.embedded_audio;
465 bool video_ready = false;
467 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
470 audio_ready = audio_frame_buffer_.try_push(frame);
473 video_ready = video_frame_buffer_.try_push(frame);
475 if (audio_ready && video_ready)
478 return boost::optional<bool>();
482 return make_ready_future(true);
484 send_completion_.set_task(enqueue_task);
486 return send_completion_.get_future();
489 std::wstring print() const
491 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_) + L"-" +
492 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
496 struct decklink_consumer_proxy : public core::frame_consumer
498 core::monitor::subject monitor_subject_;
499 const configuration config_;
500 std::unique_ptr<decklink_consumer> consumer_;
504 decklink_consumer_proxy(const configuration& config)
506 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
508 executor_.begin_invoke([=]
510 ::CoInitialize(nullptr);
514 ~decklink_consumer_proxy()
525 void initialize(const core::video_format_desc& format_desc, int channel_index) override
530 consumer_.reset(new decklink_consumer(config_, format_desc, channel_index));
534 std::future<bool> send(core::const_frame frame) override
536 return consumer_->send(frame);
539 std::wstring print() const override
541 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
544 std::wstring name() const override
549 boost::property_tree::wptree info() const override
551 boost::property_tree::wptree info;
552 info.add(L"type", L"decklink");
553 info.add(L"key-only", config_.key_only);
554 info.add(L"device", config_.device_index);
555 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
556 info.add(L"embedded-audio", config_.embedded_audio);
557 //info.add(L"internal-key", config_.internal_key);
561 int buffer_depth() const override
563 return config_.buffer_depth();
566 int index() const override
568 return 300 + config_.device_index;
571 core::monitor::subject& monitor_output()
573 return monitor_subject_;
577 spl::shared_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>& params)
579 if(params.size() < 1 || params[0] != L"DECKLINK")
580 return core::frame_consumer::empty();
582 configuration config;
584 if(params.size() > 1)
585 config.device_index = boost::lexical_cast<int>(params[1]);
587 if(std::find(params.begin(), params.end(), L"INTERNAL_KEY") != params.end())
588 config.keyer = configuration::keyer_t::internal_keyer;
589 else if(std::find(params.begin(), params.end(), L"EXTERNAL_KEY") != params.end())
590 config.keyer = configuration::keyer_t::external_keyer;
592 config.keyer = configuration::keyer_t::default_keyer;
594 if(std::find(params.begin(), params.end(), L"LOW_LATENCY") != params.end())
595 config.latency = configuration::latency_t::low_latency;
597 config.embedded_audio = std::find(params.begin(), params.end(), L"EMBEDDED_AUDIO") != params.end();
598 config.key_only = std::find(params.begin(), params.end(), L"KEY_ONLY") != params.end();
600 return spl::make_shared<decklink_consumer_proxy>(config);
603 spl::shared_ptr<core::frame_consumer> create_consumer(const boost::property_tree::wptree& ptree)
605 configuration config;
607 auto keyer = ptree.get(L"keyer", L"default");
608 if(keyer == L"external")
609 config.keyer = configuration::keyer_t::external_keyer;
610 else if(keyer == L"internal")
611 config.keyer = configuration::keyer_t::internal_keyer;
613 auto latency = ptree.get(L"latency", L"normal");
614 if(latency == L"low")
615 config.latency = configuration::latency_t::low_latency;
616 else if(latency == L"normal")
617 config.latency = configuration::latency_t::normal_latency;
619 config.key_only = ptree.get(L"key-only", config.key_only);
620 config.device_index = ptree.get(L"device", config.device_index);
621 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
622 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
624 return spl::make_shared<decklink_consumer_proxy>(config);
630 ##############################################################################
636 BMD Developer Support
637 developer@blackmagic-design.com
639 -----------------------------------------------------------------------------
641 Thanks for your inquiry. The minimum number of frames that you can preroll
642 for scheduled playback is three frames for video and four frames for audio.
643 As you mentioned if you preroll less frames then playback will not start or
644 playback will be very sporadic. From our experience with Media Express, we
645 recommended that at least seven frames are prerolled for smooth playback.
647 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
648 There can be around 3 frames worth of latency on scheduled output.
649 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
650 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
651 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
652 guarantee that the provided frame will be output as soon the previous
653 frame output has been completed.
654 ################################################################################
658 ##############################################################################
659 Async DMA Transfer without redundant copying
664 BMD Developer Support
665 developer@blackmagic-design.com
667 -----------------------------------------------------------------------------
669 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
670 and providing a pointer to your video buffer when GetBytes() is called.
671 This may help to keep copying to a minimum. Please ensure that the pixel
672 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
673 have to colourspace convert which may result in additional copying.
674 ################################################################################