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>
40 #include <core/consumer/frame_consumer.h>
42 #include <tbb/concurrent_queue.h>
43 #include <tbb/cache_aligned_allocator.h>
45 #include <common/assert.h>
46 #include <boost/lexical_cast.hpp>
47 #include <boost/circular_buffer.hpp>
48 #include <boost/timer.hpp>
49 #include <boost/property_tree/ptree.hpp>
51 namespace caspar { namespace decklink {
74 int base_buffer_depth;
78 , embedded_audio(true)
79 , keyer(default_keyer)
80 , latency(default_latency)
82 , base_buffer_depth(3)
86 int buffer_depth() const
88 return base_buffer_depth + (latency == low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
92 class decklink_frame : public IDeckLinkVideoFrame
94 tbb::atomic<int> ref_count_;
95 core::const_frame frame_;
96 const core::video_format_desc format_desc_;
99 std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> data_;
101 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
103 , format_desc_(format_desc)
104 , key_only_(key_only)
111 STDMETHOD (QueryInterface(REFIID, LPVOID*))
113 return E_NOINTERFACE;
116 STDMETHOD_(ULONG, AddRef())
121 STDMETHOD_(ULONG, Release())
123 if(--ref_count_ == 0)
128 // IDecklinkVideoFrame
130 STDMETHOD_(long, GetWidth()) {return static_cast<long>(format_desc_.width);}
131 STDMETHOD_(long, GetHeight()) {return static_cast<long>(format_desc_.height);}
132 STDMETHOD_(long, GetRowBytes()) {return static_cast<long>(format_desc_.width*4);}
133 STDMETHOD_(BMDPixelFormat, GetPixelFormat()) {return bmdFormat8BitBGRA;}
134 STDMETHOD_(BMDFrameFlags, GetFlags()) {return bmdFrameFlagDefault;}
136 STDMETHOD(GetBytes(void** buffer))
140 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
142 data_.resize(format_desc_.size, 0);
143 *buffer = data_.data();
149 data_.resize(frame_.image_data().size());
150 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
152 *buffer = data_.data();
155 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
159 CASPAR_LOG_CURRENT_EXCEPTION();
166 STDMETHOD(GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode)){return S_FALSE;}
167 STDMETHOD(GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary)) {return S_FALSE;}
171 const core::audio_buffer& audio_data()
173 return frame_.audio_data();
177 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
179 const int channel_index_;
180 const configuration config_;
182 CComPtr<IDeckLink> decklink_;
183 CComQIPtr<IDeckLinkOutput> output_;
184 CComQIPtr<IDeckLinkConfiguration> configuration_;
185 CComQIPtr<IDeckLinkKeyer> keyer_;
186 CComQIPtr<IDeckLinkAttributes> attributes_;
188 tbb::spin_mutex exception_mutex_;
189 std::exception_ptr exception_;
191 tbb::atomic<bool> is_running_;
193 const std::wstring model_name_;
194 const core::video_format_desc format_desc_;
195 const int buffer_size_;
197 long long video_scheduled_;
198 long long audio_scheduled_;
202 boost::circular_buffer<std::vector<int32_t>> audio_container_;
204 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
205 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
207 spl::shared_ptr<diagnostics::graph> graph_;
208 boost::timer tick_timer_;
211 decklink_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index)
212 : channel_index_(channel_index)
214 , decklink_(get_device(config.device_index))
216 , configuration_(decklink_)
218 , attributes_(decklink_)
219 , model_name_(get_model_name(decklink_))
220 , format_desc_(format_desc)
221 , buffer_size_(config.buffer_depth()) // Minimum buffer-size 3.
222 , video_scheduled_(0)
223 , audio_scheduled_(0)
225 , audio_container_(buffer_size_+1)
229 video_frame_buffer_.set_capacity(1);
230 audio_frame_buffer_.set_capacity(1);
232 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
233 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
234 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
235 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
236 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
237 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
238 graph_->set_text(print());
239 diagnostics::register_graph(graph_);
241 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
243 if(config.embedded_audio)
246 set_latency(config.latency);
247 set_keyer(config.keyer);
249 if(config.embedded_audio)
250 output_->BeginAudioPreroll();
252 for(int n = 0; n < buffer_size_; ++n)
253 schedule_next_video(core::const_frame::empty());
255 if(!config.embedded_audio)
262 video_frame_buffer_.try_push(core::const_frame::empty());
263 audio_frame_buffer_.try_push(core::const_frame::empty());
265 if(output_ != nullptr)
267 output_->StopScheduledPlayback(0, nullptr, 0);
268 if(config_.embedded_audio)
269 output_->DisableAudioOutput();
270 output_->DisableVideoOutput();
274 void set_latency(configuration::latency_t latency)
276 if(latency == configuration::low_latency)
278 configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
279 CASPAR_LOG(info) << print() << L" Enabled low-latency mode.";
281 else if(latency == configuration::normal_latency)
283 configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
284 CASPAR_LOG(info) << print() << L" Disabled low-latency mode.";
288 void set_keyer(configuration::keyer_t keyer)
290 if(keyer == configuration::internal_keyer)
293 if(SUCCEEDED(attributes_->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
294 CASPAR_LOG(error) << print() << L" Failed to enable internal keyer.";
295 else if(FAILED(keyer_->Enable(FALSE)))
296 CASPAR_LOG(error) << print() << L" Failed to enable internal keyer.";
297 else if(FAILED(keyer_->SetLevel(255)))
298 CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";
300 CASPAR_LOG(info) << print() << L" Enabled internal keyer.";
302 else if(keyer == configuration::external_keyer)
305 if(SUCCEEDED(attributes_->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
306 CASPAR_LOG(error) << print() << L" Failed to enable external keyer.";
307 else if(FAILED(keyer_->Enable(TRUE)))
308 CASPAR_LOG(error) << print() << L" Failed to enable external keyer.";
309 else if(FAILED(keyer_->SetLevel(255)))
310 CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";
312 CASPAR_LOG(info) << print() << L" Enabled external keyer.";
318 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))
319 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable audio output."));
321 if(FAILED(output_->SetAudioCallback(this)))
322 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not set audio callback."));
324 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
327 void enable_video(BMDDisplayMode display_mode)
329 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
330 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable video output."));
332 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
333 CASPAR_THROW_EXCEPTION(caspar_exception()
334 << msg_info(u8(print()) + " Failed to set playback completion callback.")
335 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
338 void start_playback()
340 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
341 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule playback."));
344 STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;}
345 STDMETHOD_(ULONG, AddRef()) {return 1;}
346 STDMETHOD_(ULONG, Release()) {return 1;}
348 STDMETHOD(ScheduledPlaybackHasStopped())
351 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
355 STDMETHOD(ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result))
362 if(result == bmdOutputFrameDisplayedLate)
364 graph_->set_tag("late-frame");
365 video_scheduled_ += format_desc_.duration;
366 audio_scheduled_ += reinterpret_cast<decklink_frame*>(completed_frame)->audio_data().size()/format_desc_.audio_channels;
367 //++video_scheduled_;
368 //audio_scheduled_ += format_desc_.audio_cadence[0];
369 //++audio_scheduled_;
371 else if(result == bmdOutputFrameDropped)
372 graph_->set_tag("dropped-frame");
373 else if(result == bmdOutputFrameFlushed)
374 graph_->set_tag("flushed-frame");
376 auto frame = core::const_frame::empty();
377 video_frame_buffer_.pop(frame);
378 schedule_next_video(frame);
380 unsigned long buffered;
381 output_->GetBufferedVideoFrameCount(&buffered);
382 graph_->set_value("buffered-video", static_cast<double>(buffered)/format_desc_.fps);
386 lock(exception_mutex_, [&]
388 exception_ = std::current_exception();
396 STDMETHOD(RenderAudioSamples(BOOL preroll))
405 if(++preroll_count_ >= buffer_size_)
407 output_->EndAudioPreroll();
411 schedule_next_audio(core::audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()], 0));
415 auto frame = core::const_frame::empty();
416 audio_frame_buffer_.pop(frame);
417 schedule_next_audio(frame.audio_data());
420 unsigned long buffered;
421 output_->GetBufferedAudioSampleFrameCount(&buffered);
422 graph_->set_value("buffered-audio", static_cast<double>(buffered)/(format_desc_.audio_cadence[0]*2));
426 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
427 exception_ = std::current_exception();
435 void schedule_next_audio(const T& audio_data)
437 auto sample_frame_count = static_cast<int>(audio_data.size()/format_desc_.audio_channels);
439 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
441 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
442 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
444 audio_scheduled_ += sample_frame_count;
447 void schedule_next_video(core::const_frame frame)
449 CComPtr<IDeckLinkVideoFrame> frame2(new decklink_frame(frame, format_desc_, config_.key_only));
450 if(FAILED(output_->ScheduleVideoFrame(frame2, video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
451 CASPAR_LOG(error) << print() << L" Failed to schedule video.";
453 video_scheduled_ += format_desc_.duration;
455 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
456 tick_timer_.restart();
459 void send(core::const_frame frame)
461 auto exception = lock(exception_mutex_, [&]
466 if(exception != nullptr)
467 std::rethrow_exception(exception);
470 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Is not running."));
472 if(config_.embedded_audio)
473 audio_frame_buffer_.push(frame);
474 video_frame_buffer_.push(frame);
477 std::wstring print() const
479 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_) + L"-" +
480 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
484 struct decklink_consumer_proxy : public core::frame_consumer
486 const configuration config_;
487 std::unique_ptr<decklink_consumer> consumer_;
491 decklink_consumer_proxy(const configuration& config)
493 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
495 executor_.begin_invoke([=]
497 ::CoInitialize(nullptr);
501 ~decklink_consumer_proxy()
511 void initialize(const core::video_format_desc& format_desc, int channel_index) override
516 consumer_.reset(new decklink_consumer(config_, format_desc, channel_index));
520 bool send(core::const_frame frame) override
522 consumer_->send(frame);
526 std::wstring print() const override
528 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
531 std::wstring name() const override
536 boost::property_tree::wptree info() const override
538 boost::property_tree::wptree info;
539 info.add(L"type", L"decklink");
540 info.add(L"key-only", config_.key_only);
541 info.add(L"device", config_.device_index);
542 info.add(L"low-latency", config_.low_latency);
543 info.add(L"embedded-audio", config_.embedded_audio);
544 info.add(L"low-latency", config_.low_latency);
545 //info.add(L"internal-key", config_.internal_key);
549 int buffer_depth() const override
551 return config_.buffer_depth();
554 int index() const override
556 return 300 + config_.device_index;
559 void subscribe(const monitor::observable::observer_ptr& o) override
563 void unsubscribe(const monitor::observable::observer_ptr& o) override
568 spl::shared_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>& params)
570 if(params.size() < 1 || params[0] != L"DECKLINK")
571 return core::frame_consumer::empty();
573 configuration config;
575 if(params.size() > 1)
576 config.device_index = boost::lexical_cast<int>(params[1]);
578 if(std::find(params.begin(), params.end(), L"INTERNAL_KEY") != params.end())
579 config.keyer = configuration::internal_keyer;
580 else if(std::find(params.begin(), params.end(), L"EXTERNAL_KEY") != params.end())
581 config.keyer = configuration::external_keyer;
583 config.keyer = configuration::default_keyer;
585 if(std::find(params.begin(), params.end(), L"LOW_LATENCY") != params.end())
586 config.latency = configuration::low_latency;
588 config.embedded_audio = std::find(params.begin(), params.end(), L"EMBEDDED_AUDIO") != params.end();
589 config.key_only = std::find(params.begin(), params.end(), L"KEY_ONLY") != params.end();
591 return spl::make_shared<decklink_consumer_proxy>(config);
594 spl::shared_ptr<core::frame_consumer> create_consumer(const boost::property_tree::wptree& ptree)
596 configuration config;
598 auto keyer = ptree.get(L"keyer", L"default");
599 if(keyer == L"external")
600 config.keyer = configuration::external_keyer;
601 else if(keyer == L"internal")
602 config.keyer = configuration::internal_keyer;
604 auto latency = ptree.get(L"latency", L"normal");
605 if(latency == L"low")
606 config.latency = configuration::low_latency;
607 else if(latency == L"normal")
608 config.latency = configuration::normal_latency;
610 config.key_only = ptree.get(L"key-only", config.key_only);
611 config.device_index = ptree.get(L"device", config.device_index);
612 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
613 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
615 return spl::make_shared<decklink_consumer_proxy>(config);
621 ##############################################################################
627 BMD Developer Support
628 developer@blackmagic-design.com
630 -----------------------------------------------------------------------------
632 Thanks for your inquiry. The minimum number of frames that you can preroll
633 for scheduled playback is three frames for video and four frames for audio.
634 As you mentioned if you preroll less frames then playback will not start or
635 playback will be very sporadic. From our experience with Media Express, we
636 recommended that at least seven frames are prerolled for smooth playback.
638 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
639 There can be around 3 frames worth of latency on scheduled output.
640 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
641 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
642 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
643 guarantee that the provided frame will be output as soon the previous
644 frame output has been completed.
645 ################################################################################
649 ##############################################################################
650 Async DMA Transfer without redundant copying
655 BMD Developer Support
656 developer@blackmagic-design.com
658 -----------------------------------------------------------------------------
660 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
661 and providing a pointer to your video buffer when GetBytes() is called.
662 This may help to keep copying to a minimum. Please ensure that the pixel
663 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
664 have to colourspace convert which may result in additional copying.
665 ################################################################################