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>
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>
40 #include <common/cache_aligned_vector.h>
41 #include <common/timer.h>
42 #include <common/param.h>
44 #include <core/consumer/frame_consumer.h>
45 #include <core/diagnostics/call_context.h>
47 #include <tbb/concurrent_queue.h>
49 #include <common/assert.h>
50 #include <boost/lexical_cast.hpp>
51 #include <boost/circular_buffer.hpp>
52 #include <boost/property_tree/ptree.hpp>
54 namespace caspar { namespace decklink {
73 bool embedded_audio = true;
74 keyer_t keyer = keyer_t::default_keyer;
75 latency_t latency = latency_t::default_latency;
76 bool key_only = false;
77 int base_buffer_depth = 3;
79 int buffer_depth() const
81 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
85 class decklink_frame : public IDeckLinkVideoFrame
87 tbb::atomic<int> ref_count_;
88 core::const_frame frame_;
89 const core::video_format_desc format_desc_;
92 cache_aligned_vector<uint8_t> data_;
94 decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
96 , format_desc_(format_desc)
104 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
106 return E_NOINTERFACE;
109 virtual ULONG STDMETHODCALLTYPE AddRef()
114 virtual ULONG STDMETHODCALLTYPE Release()
116 if(--ref_count_ == 0)
121 // IDecklinkVideoFrame
123 virtual long STDMETHODCALLTYPE GetWidth() {return static_cast<long>(format_desc_.width);}
124 virtual long STDMETHODCALLTYPE GetHeight() {return static_cast<long>(format_desc_.height);}
125 virtual long STDMETHODCALLTYPE GetRowBytes() {return static_cast<long>(format_desc_.width*4);}
126 virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat() {return bmdFormat8BitBGRA;}
127 virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags() {return bmdFrameFlagDefault;}
129 virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
133 if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
135 data_.resize(format_desc_.size, 0);
136 *buffer = data_.data();
142 data_.resize(frame_.image_data().size());
143 aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
145 *buffer = data_.data();
148 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
152 CASPAR_LOG_CURRENT_EXCEPTION();
159 virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
160 virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary) {return S_FALSE;}
164 const core::audio_buffer& audio_data()
166 return frame_.audio_data();
170 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
172 const int channel_index_;
173 const configuration config_;
175 com_ptr<IDeckLink> decklink_ = get_device(config_.device_index);
176 com_iface_ptr<IDeckLinkOutput> output_ = iface_cast<IDeckLinkOutput>(decklink_);
177 com_iface_ptr<IDeckLinkConfiguration> configuration_ = iface_cast<IDeckLinkConfiguration>(decklink_);
178 com_iface_ptr<IDeckLinkKeyer> keyer_ = iface_cast<IDeckLinkKeyer>(decklink_);
179 com_iface_ptr<IDeckLinkAttributes> attributes_ = iface_cast<IDeckLinkAttributes>(decklink_);
181 tbb::spin_mutex exception_mutex_;
182 std::exception_ptr exception_;
184 tbb::atomic<bool> is_running_;
186 const std::wstring model_name_ = get_model_name(decklink_);
187 const core::video_format_desc format_desc_;
188 const int buffer_size_ = config_.buffer_depth(); // Minimum buffer-size 3.
190 long long video_scheduled_ = 0;
191 long long audio_scheduled_ = 0;
193 int preroll_count_ = 0;
195 boost::circular_buffer<std::vector<int32_t>> audio_container_ { buffer_size_ + 1 };
197 tbb::concurrent_bounded_queue<core::const_frame> video_frame_buffer_;
198 tbb::concurrent_bounded_queue<core::const_frame> audio_frame_buffer_;
200 spl::shared_ptr<diagnostics::graph> graph_;
201 caspar::timer tick_timer_;
202 retry_task<bool> send_completion_;
205 decklink_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index)
206 : channel_index_(channel_index)
208 , format_desc_(format_desc)
212 video_frame_buffer_.set_capacity(1);
214 // Blackmagic calls RenderAudioSamples() 50 times per second
215 // regardless of video mode so we sometimes need to give them
216 // samples from 2 frames in order to keep up
217 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
219 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
220 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
221 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
222 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
223 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
224 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
225 graph_->set_text(print());
226 diagnostics::register_graph(graph_);
228 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
230 if(config.embedded_audio)
233 set_latency(config.latency);
234 set_keyer(config.keyer);
236 if(config.embedded_audio)
237 output_->BeginAudioPreroll();
239 for(int n = 0; n < buffer_size_; ++n)
240 schedule_next_video(core::const_frame::empty());
242 if(!config.embedded_audio)
249 video_frame_buffer_.try_push(core::const_frame::empty());
250 audio_frame_buffer_.try_push(core::const_frame::empty());
252 if(output_ != nullptr)
254 output_->StopScheduledPlayback(0, nullptr, 0);
255 if(config_.embedded_audio)
256 output_->DisableAudioOutput();
257 output_->DisableVideoOutput();
261 void set_latency(configuration::latency_t latency)
263 if(latency == configuration::latency_t::low_latency)
265 configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
266 CASPAR_LOG(info) << print() << L" Enabled low-latency mode.";
268 else if(latency == configuration::latency_t::normal_latency)
270 configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
271 CASPAR_LOG(info) << print() << L" Disabled low-latency mode.";
275 void set_keyer(configuration::keyer_t keyer)
277 if(keyer == configuration::keyer_t::internal_keyer)
280 if(SUCCEEDED(attributes_->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
281 CASPAR_LOG(error) << print() << L" Failed to enable internal keyer.";
282 else if(FAILED(keyer_->Enable(FALSE)))
283 CASPAR_LOG(error) << print() << L" Failed to enable internal keyer.";
284 else if(FAILED(keyer_->SetLevel(255)))
285 CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";
287 CASPAR_LOG(info) << print() << L" Enabled internal keyer.";
289 else if(keyer == configuration::keyer_t::external_keyer)
292 if(SUCCEEDED(attributes_->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
293 CASPAR_LOG(error) << print() << L" Failed to enable external keyer.";
294 else if(FAILED(keyer_->Enable(TRUE)))
295 CASPAR_LOG(error) << print() << L" Failed to enable external keyer.";
296 else if(FAILED(keyer_->SetLevel(255)))
297 CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";
299 CASPAR_LOG(info) << print() << L" Enabled external keyer.";
305 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))
306 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable audio output."));
308 if(FAILED(output_->SetAudioCallback(this)))
309 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not set audio callback."));
311 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
314 void enable_video(BMDDisplayMode display_mode)
316 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
317 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable video output."));
319 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
320 CASPAR_THROW_EXCEPTION(caspar_exception()
321 << msg_info(u8(print()) + " Failed to set playback completion callback.")
322 << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
325 void start_playback()
327 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
328 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule playback."));
331 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
332 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
333 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
335 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
338 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
342 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
349 if(result == bmdOutputFrameDisplayedLate)
351 graph_->set_tag("late-frame");
352 video_scheduled_ += format_desc_.duration;
353 audio_scheduled_ += reinterpret_cast<decklink_frame*>(completed_frame)->audio_data().size()/format_desc_.audio_channels;
354 //++video_scheduled_;
355 //audio_scheduled_ += format_desc_.audio_cadence[0];
356 //++audio_scheduled_;
358 else if(result == bmdOutputFrameDropped)
359 graph_->set_tag("dropped-frame");
360 else if(result == bmdOutputFrameFlushed)
361 graph_->set_tag("flushed-frame");
363 auto frame = core::const_frame::empty();
364 video_frame_buffer_.pop(frame);
365 send_completion_.try_completion();
366 schedule_next_video(frame);
369 output_->GetBufferedVideoFrameCount(&buffered);
370 graph_->set_value("buffered-video", static_cast<double>(buffered)/format_desc_.fps);
374 lock(exception_mutex_, [&]
376 exception_ = std::current_exception();
384 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
393 if(++preroll_count_ >= buffer_size_)
395 output_->EndAudioPreroll();
400 schedule_next_audio(core::audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * format_desc_.audio_channels, 0));
405 auto frame = core::const_frame::empty();
407 while(audio_frame_buffer_.try_pop(frame))
409 send_completion_.try_completion();
410 schedule_next_audio(frame.audio_data());
415 output_->GetBufferedAudioSampleFrameCount(&buffered);
416 graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * format_desc_.audio_channels * 2));
420 tbb::spin_mutex::scoped_lock lock(exception_mutex_);
421 exception_ = std::current_exception();
429 void schedule_next_audio(const T& audio_data)
431 auto sample_frame_count = static_cast<int>(audio_data.size()/format_desc_.audio_channels);
433 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
435 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
436 CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
438 audio_scheduled_ += sample_frame_count;
441 void schedule_next_video(core::const_frame frame)
443 auto frame2 = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only));
444 if(FAILED(output_->ScheduleVideoFrame(get_raw(frame2), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
445 CASPAR_LOG(error) << print() << L" Failed to schedule video.";
447 video_scheduled_ += format_desc_.duration;
449 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
450 tick_timer_.restart();
453 std::future<bool> send(core::const_frame frame)
455 auto exception = lock(exception_mutex_, [&]
460 if(exception != nullptr)
461 std::rethrow_exception(exception);
464 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Is not running."));
466 bool audio_ready = !config_.embedded_audio;
467 bool video_ready = false;
469 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
472 audio_ready = audio_frame_buffer_.try_push(frame);
475 video_ready = video_frame_buffer_.try_push(frame);
477 if (audio_ready && video_ready)
480 return boost::optional<bool>();
484 return make_ready_future(true);
486 send_completion_.set_task(enqueue_task);
488 return send_completion_.get_future();
491 std::wstring print() const
493 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_) + L"-" +
494 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
498 struct decklink_consumer_proxy : public core::frame_consumer
500 core::monitor::subject monitor_subject_;
501 const configuration config_;
502 std::unique_ptr<decklink_consumer> consumer_;
506 decklink_consumer_proxy(const configuration& config)
508 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
510 auto ctx = core::diagnostics::call_context::for_thread();
511 executor_.begin_invoke([=]
513 core::diagnostics::call_context::for_thread() = ctx;
518 ~decklink_consumer_proxy()
529 void initialize(const core::video_format_desc& format_desc, int channel_index) override
534 consumer_.reset(new decklink_consumer(config_, format_desc, channel_index));
538 std::future<bool> send(core::const_frame frame) override
540 return consumer_->send(frame);
543 std::wstring print() const override
545 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
548 std::wstring name() const override
553 boost::property_tree::wptree info() const override
555 boost::property_tree::wptree info;
556 info.add(L"type", L"decklink");
557 info.add(L"key-only", config_.key_only);
558 info.add(L"device", config_.device_index);
559 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
560 info.add(L"embedded-audio", config_.embedded_audio);
561 //info.add(L"internal-key", config_.internal_key);
565 int buffer_depth() const override
567 return config_.buffer_depth();
570 int index() const override
572 return 300 + config_.device_index;
575 core::monitor::subject& monitor_output()
577 return monitor_subject_;
581 spl::shared_ptr<core::frame_consumer> create_consumer(
582 const std::vector<std::wstring>& params, core::interaction_sink*)
584 if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
585 return core::frame_consumer::empty();
587 configuration config;
589 if (params.size() > 1)
590 config.device_index = boost::lexical_cast<int>(params.at(1));
592 if (contains_param(L"INTERNAL_KEY", params))
593 config.keyer = configuration::keyer_t::internal_keyer;
594 else if (contains_param(L"EXTERNAL_KEY", params))
595 config.keyer = configuration::keyer_t::external_keyer;
597 config.keyer = configuration::keyer_t::default_keyer;
599 if (contains_param(L"LOW_LATENCY", params))
600 config.latency = configuration::latency_t::low_latency;
602 config.embedded_audio = contains_param(L"EMBEDDED_AUDIO", params);
603 config.key_only = contains_param(L"KEY_ONLY", params);
605 return spl::make_shared<decklink_consumer_proxy>(config);
608 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
609 const boost::property_tree::wptree& ptree, core::interaction_sink*)
611 configuration config;
613 auto keyer = ptree.get(L"keyer", L"default");
614 if(keyer == L"external")
615 config.keyer = configuration::keyer_t::external_keyer;
616 else if(keyer == L"internal")
617 config.keyer = configuration::keyer_t::internal_keyer;
619 auto latency = ptree.get(L"latency", L"normal");
620 if(latency == L"low")
621 config.latency = configuration::latency_t::low_latency;
622 else if(latency == L"normal")
623 config.latency = configuration::latency_t::normal_latency;
625 config.key_only = ptree.get(L"key-only", config.key_only);
626 config.device_index = ptree.get(L"device", config.device_index);
627 config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);
628 config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);
630 return spl::make_shared<decklink_consumer_proxy>(config);
636 ##############################################################################
642 BMD Developer Support
643 developer@blackmagic-design.com
645 -----------------------------------------------------------------------------
647 Thanks for your inquiry. The minimum number of frames that you can preroll
648 for scheduled playback is three frames for video and four frames for audio.
649 As you mentioned if you preroll less frames then playback will not start or
650 playback will be very sporadic. From our experience with Media Express, we
651 recommended that at least seven frames are prerolled for smooth playback.
653 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
654 There can be around 3 frames worth of latency on scheduled output.
655 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
656 reduced or removed for scheduled playback. If the DisplayVideoFrameSync()
657 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will
658 guarantee that the provided frame will be output as soon the previous
659 frame output has been completed.
660 ################################################################################
664 ##############################################################################
665 Async DMA Transfer without redundant copying
670 BMD Developer Support
671 developer@blackmagic-design.com
673 -----------------------------------------------------------------------------
675 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame
676 and providing a pointer to your video buffer when GetBytes() is called.
677 This may help to keep copying to a minimum. Please ensure that the pixel
678 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will
679 have to colourspace convert which may result in additional copying.
680 ################################################################################