]> git.sesse.net Git - casparcg/blob - modules/decklink/consumer/decklink_consumer.cpp
Merge pull request #374 from hummelstrand/readme-2.1.0-update
[casparcg] / modules / decklink / consumer / decklink_consumer.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 */
21
22 #include "../StdAfx.h"
23  
24 #include "decklink_consumer.h"
25
26 #include "../util/util.h"
27
28 #include "../decklink_api.h"
29
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>
36
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>
47
48 #include <tbb/concurrent_queue.h>
49
50 #include <common/assert.h>
51 #include <boost/lexical_cast.hpp>
52 #include <boost/circular_buffer.hpp>
53 #include <boost/property_tree/ptree.hpp>
54
55 namespace caspar { namespace decklink { 
56         
57 struct configuration
58 {
59         enum class keyer_t
60         {
61                 internal_keyer,
62                 external_keyer,
63                 external_separate_device_keyer,
64                 default_keyer
65         };
66
67         enum class latency_t
68         {
69                 low_latency,
70                 normal_latency,
71                 default_latency
72         };
73
74         int                     device_index            = 1;
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;
81         
82         int buffer_depth() const
83         {
84                 return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
85         }
86
87         int key_device_index() const
88         {
89                 return key_device_idx == 0 ? device_index + 1 : key_device_idx;
90         }
91 };
92
93 void set_latency(
94                 const com_iface_ptr<IDeckLinkConfiguration>& config,
95                 configuration::latency_t latency,
96                 const std::wstring& print)
97 {
98         if (latency == configuration::latency_t::low_latency)
99         {
100                 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
101                 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";
102         }
103         else if (latency == configuration::latency_t::normal_latency)
104         {
105                 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
106                 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";
107         }
108 }
109
110 void set_keyer(
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)
115 {
116         if (keyer == configuration::keyer_t::internal_keyer)
117         {
118                 BOOL value = true;
119                 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
120                         CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";     
121                 else if (FAILED(decklink_keyer->Enable(FALSE)))
122                         CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";                     
123                 else if (FAILED(decklink_keyer->SetLevel(255)))
124                         CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
125                 else
126                         CASPAR_LOG(info) << print << L" Enabled internal keyer.";               
127         }
128         else if (keyer == configuration::keyer_t::external_keyer)
129         {
130                 BOOL value = true;
131                 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
132                         CASPAR_LOG(error) << print << L" Failed to enable external keyer.";     
133                 else if (FAILED(decklink_keyer->Enable(TRUE)))                  
134                         CASPAR_LOG(error) << print << L" Failed to enable external keyer.";     
135                 else if (FAILED(decklink_keyer->SetLevel(255)))
136                         CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
137                 else
138                         CASPAR_LOG(info) << print << L" Enabled external keyer.";                       
139         }
140 }
141
142 class decklink_frame : public IDeckLinkVideoFrame
143 {
144         tbb::atomic<int>                                ref_count_;
145         core::const_frame                               frame_;
146         const core::video_format_desc   format_desc_;
147
148         const bool                                              key_only_;
149         cache_aligned_vector<uint8_t>   data_;
150 public:
151         decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
152                 : frame_(frame)
153                 , format_desc_(format_desc)
154                 , key_only_(key_only)
155         {
156                 ref_count_ = 0;
157         }
158         
159         // IUnknown
160
161         virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
162         {
163                 return E_NOINTERFACE;
164         }
165         
166         virtual ULONG STDMETHODCALLTYPE AddRef()
167         {
168                 return ++ref_count_;
169         }
170
171         virtual ULONG STDMETHODCALLTYPE Release()
172         {
173                 if(--ref_count_ == 0)
174                         delete this;
175                 return ref_count_;
176         }
177
178         // IDecklinkVideoFrame
179
180         virtual long STDMETHODCALLTYPE GetWidth()                   {return static_cast<long>(format_desc_.width);}
181         virtual long STDMETHODCALLTYPE GetHeight()                  {return static_cast<long>(format_desc_.height);}
182         virtual long STDMETHODCALLTYPE GetRowBytes()                {return static_cast<long>(format_desc_.width*4);}
183         virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat()   {return bmdFormat8BitBGRA;}
184         virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags()                      {return bmdFrameFlagDefault;}
185                 
186         virtual HRESULT STDMETHODCALLTYPE GetBytes(void** buffer)
187         {
188                 try
189                 {
190                         if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
191                         {
192                                 data_.resize(format_desc_.size, 0);
193                                 *buffer = data_.data();
194                         }
195                         else if(key_only_)
196                         {
197                                 if(data_.empty())
198                                 {
199                                         data_.resize(frame_.image_data().size());
200                                         aligned_memshfl(data_.data(), frame_.image_data().begin(), frame_.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
201                                 }
202                                 *buffer = data_.data();
203                         }
204                         else
205                                 *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
206                 }
207                 catch(...)
208                 {
209                         CASPAR_LOG_CURRENT_EXCEPTION();
210                         return E_FAIL;
211                 }
212
213                 return S_OK;
214         }
215                 
216         virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode) {return S_FALSE;}
217         virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary)              {return S_FALSE;}
218
219         // decklink_frame       
220
221         const core::audio_buffer& audio_data()
222         {
223                 return frame_.audio_data();
224         }
225
226         int64_t get_age_millis() const
227         {
228                 return frame_.get_age_millis();
229         }
230 };
231
232 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
233 {
234         const configuration                                                                             config_;
235         com_ptr<IDeckLink>                                                                              decklink_                                       = get_device(config_.key_device_index());
236         com_iface_ptr<IDeckLinkOutput>                                                  output_                                         = iface_cast<IDeckLinkOutput>(decklink_);
237         com_iface_ptr<IDeckLinkKeyer>                                                   keyer_                                          = iface_cast<IDeckLinkKeyer>(decklink_);
238         com_iface_ptr<IDeckLinkAttributes>                                              attributes_                                     = iface_cast<IDeckLinkAttributes>(decklink_);
239         com_iface_ptr<IDeckLinkConfiguration>                                   configuration_                          = iface_cast<IDeckLinkConfiguration>(decklink_);
240         tbb::atomic<int64_t>                                                                    current_presentation_delay_;
241         tbb::atomic<int64_t>                                                                    scheduled_frames_completed_;
242
243         key_video_context(const configuration& config, const std::wstring& print)
244                 : config_(config)
245         {
246                 current_presentation_delay_ = 0;
247                 scheduled_frames_completed_ = 0;
248
249                 set_latency(configuration_, config.latency, print);
250                 set_keyer(attributes_, keyer_, config.keyer, print);
251
252                 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
253                         CASPAR_THROW_EXCEPTION(caspar_exception()
254                                         << msg_info(u8(print) + " Failed to set key playback completion callback.")
255                                         << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
256         }
257
258         template<typename Print>
259         void enable_video(BMDDisplayMode display_mode, const Print& print)
260         {
261                 if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
262                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable key video output."));
263
264                 if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
265                         CASPAR_THROW_EXCEPTION(caspar_exception()
266                                         << msg_info(u8(print()) + " Failed to set key playback completion callback.")
267                                         << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
268         }
269
270         virtual ~key_video_context()
271         {
272                 if (output_)
273                 {
274                         output_->StopScheduledPlayback(0, nullptr, 0);
275                         output_->DisableVideoOutput();
276                 }
277         }
278
279         virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)       {return E_NOINTERFACE;}
280         virtual ULONG STDMETHODCALLTYPE AddRef()                                                        {return 1;}
281         virtual ULONG STDMETHODCALLTYPE Release()                                                       {return 1;}
282
283         virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
284         {
285                 return S_OK;
286         }
287
288         virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
289                         IDeckLinkVideoFrame* completed_frame,
290                         BMDOutputFrameCompletionResult result)
291         {
292                 auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
293                 current_presentation_delay_ = dframe->get_age_millis();
294                 ++scheduled_frames_completed_;
295
296                 // Let the fill callback keep the pace, so no scheduling here.
297
298                 return S_OK;
299         }
300 };
301
302 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
303 {               
304         const int                                                                                       channel_index_;
305         const configuration                                                                     config_;
306
307         com_ptr<IDeckLink>                                                                      decklink_                               = get_device(config_.device_index);
308         com_iface_ptr<IDeckLinkOutput>                                          output_                                 = iface_cast<IDeckLinkOutput>(decklink_);
309         com_iface_ptr<IDeckLinkConfiguration>                           configuration_                  = iface_cast<IDeckLinkConfiguration>(decklink_);
310         com_iface_ptr<IDeckLinkKeyer>                                           keyer_                                  = iface_cast<IDeckLinkKeyer>(decklink_);
311         com_iface_ptr<IDeckLinkAttributes>                                      attributes_                             = iface_cast<IDeckLinkAttributes>(decklink_);
312
313         tbb::spin_mutex                                                                         exception_mutex_;
314         std::exception_ptr                                                                      exception_;
315
316         tbb::atomic<bool>                                                                       is_running_;
317                 
318         const std::wstring                                                                      model_name_                             = get_model_name(decklink_);
319         const core::video_format_desc                                           format_desc_;
320         const int                                                                                       buffer_size_                    = config_.buffer_depth(); // Minimum buffer-size 3.
321
322         long long                                                                                       video_scheduled_                = 0;
323         long long                                                                                       audio_scheduled_                = 0;
324
325         int                                                                                                     preroll_count_                  = 0;
326                 
327         boost::circular_buffer<std::vector<int32_t>>            audio_container_                { buffer_size_ + 1 };
328
329         tbb::concurrent_bounded_queue<core::const_frame>        video_frame_buffer_;
330         tbb::concurrent_bounded_queue<core::const_frame>        audio_frame_buffer_;
331         
332         spl::shared_ptr<diagnostics::graph>                                     graph_;
333         tbb::atomic<int64_t>                                                            current_presentation_delay_;
334         caspar::timer                                                                           tick_timer_;
335         retry_task<bool>                                                                        send_completion_;
336         tbb::atomic<int64_t>                                                            scheduled_frames_completed_;
337         std::unique_ptr<key_video_context>                                      key_context_;
338
339 public:
340         decklink_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index) 
341                 : channel_index_(channel_index)
342                 , config_(config)
343                 , format_desc_(format_desc)
344         {
345                 is_running_ = true;
346                 current_presentation_delay_ = 0;
347                 scheduled_frames_completed_ = 0;
348                                 
349                 video_frame_buffer_.set_capacity(1);
350
351                 // Blackmagic calls RenderAudioSamples() 50 times per second
352                 // regardless of video mode so we sometimes need to give them
353                 // samples from 2 frames in order to keep up
354                 audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
355
356                 if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
357                         key_context_.reset(new key_video_context(config, print()));
358
359                 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   
360                 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
361                 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
362                 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));
363                 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));
364                 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));
365
366                 if (key_context_)
367                 {
368                         graph_->set_color("key-offset", diagnostics::color(1.0f, 0.0f, 0.0f));
369                 }
370
371                 graph_->set_text(print());
372                 diagnostics::register_graph(graph_);
373                 
374                 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
375                                 
376                 if(config.embedded_audio)
377                         enable_audio();
378                 
379                 set_latency(configuration_, config.latency, print());                           
380                 set_keyer(attributes_, keyer_, config.keyer, print());
381
382                 if(config.embedded_audio)               
383                         output_->BeginAudioPreroll();           
384                 
385                 for(int n = 0; n < buffer_size_; ++n)
386                         schedule_next_video(core::const_frame::empty());
387
388                 if(!config.embedded_audio)
389                         start_playback();
390         }
391
392         ~decklink_consumer()
393         {               
394                 is_running_ = false;
395                 video_frame_buffer_.try_push(core::const_frame::empty());
396                 audio_frame_buffer_.try_push(core::const_frame::empty());
397
398                 if(output_ != nullptr) 
399                 {
400                         output_->StopScheduledPlayback(0, nullptr, 0);
401                         if(config_.embedded_audio)
402                                 output_->DisableAudioOutput();
403                         output_->DisableVideoOutput();
404                 }
405         }
406         
407         void enable_audio()
408         {
409                 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))
410                                 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable audio output."));
411                                 
412                 if(FAILED(output_->SetAudioCallback(this)))
413                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not set audio callback."));
414
415                 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
416         }
417
418         void enable_video(BMDDisplayMode display_mode)
419         {
420                 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault))) 
421                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable fill video output."));
422                 
423                 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
424                         CASPAR_THROW_EXCEPTION(caspar_exception() 
425                                                                         << msg_info(u8(print()) + " Failed to set fill playback completion callback.")
426                                                                         << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
427
428                 if (key_context_)
429                         key_context_->enable_video(display_mode, [this]() { return print(); });
430         }
431
432         void start_playback()
433         {
434                 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0))) 
435                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule fill playback."));
436
437                 if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
438                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule key playback."));
439         }
440         
441         virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)       {return E_NOINTERFACE;}
442         virtual ULONG STDMETHODCALLTYPE AddRef()                                        {return 1;}
443         virtual ULONG STDMETHODCALLTYPE Release()                               {return 1;}
444         
445         virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
446         {
447                 is_running_ = false;
448                 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";
449                 return S_OK;
450         }
451
452         virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result)
453         {
454                 if(!is_running_)
455                         return E_FAIL;
456                 
457                 try
458                 {
459                         auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
460                         current_presentation_delay_ = dframe->get_age_millis();
461                         ++scheduled_frames_completed_;
462
463                         if (key_context_)
464                                 graph_->set_value(
465                                                 "key-offset",
466                                                 static_cast<double>(
467                                                                 scheduled_frames_completed_
468                                                                 - key_context_->scheduled_frames_completed_)
469                                                 * 0.1 + 0.5);
470
471                         if(result == bmdOutputFrameDisplayedLate)
472                         {
473                                 graph_->set_tag("late-frame");
474                                 video_scheduled_ += format_desc_.duration;
475                                 audio_scheduled_ += dframe->audio_data().size() / format_desc_.audio_channels;
476                                 //++video_scheduled_;
477                                 //audio_scheduled_ += format_desc_.audio_cadence[0];
478                                 //++audio_scheduled_;
479                         }
480                         else if(result == bmdOutputFrameDropped)
481                                 graph_->set_tag("dropped-frame");
482                         else if(result == bmdOutputFrameFlushed)
483                                 graph_->set_tag("flushed-frame");
484
485                         auto frame = core::const_frame::empty();        
486                         video_frame_buffer_.pop(frame);
487                         send_completion_.try_completion();
488                         schedule_next_video(frame);     
489                         
490                         UINT32 buffered;
491                         output_->GetBufferedVideoFrameCount(&buffered);
492                         graph_->set_value("buffered-video", static_cast<double>(buffered)/format_desc_.fps);
493                 }
494                 catch(...)
495                 {
496                         lock(exception_mutex_, [&]
497                         {
498                                 exception_ = std::current_exception();
499                         });
500                         return E_FAIL;
501                 }
502
503                 return S_OK;
504         }
505                 
506         virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
507         {
508                 if(!is_running_)
509                         return E_FAIL;
510                 
511                 try
512                 {       
513                         if(preroll)
514                         {
515                                 if(++preroll_count_ >= buffer_size_)
516                                 {
517                                         output_->EndAudioPreroll();
518                                         start_playback();                               
519                                 }
520                                 else
521                                 {
522                                         schedule_next_audio(core::audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * format_desc_.audio_channels, 0));
523                                 }
524                         }
525                         else
526                         {
527                                 auto frame = core::const_frame::empty();
528
529                                 while(audio_frame_buffer_.try_pop(frame))
530                                 {
531                                         send_completion_.try_completion();
532                                         schedule_next_audio(frame.audio_data());
533                                 }
534                         }
535
536                         UINT32 buffered;
537                         output_->GetBufferedAudioSampleFrameCount(&buffered);
538                         graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * format_desc_.audio_channels * 2));
539                 }
540                 catch(...)
541                 {
542                         tbb::spin_mutex::scoped_lock lock(exception_mutex_);
543                         exception_ = std::current_exception();
544                         return E_FAIL;
545                 }
546
547                 return S_OK;
548         }
549
550         template<typename T>
551         void schedule_next_audio(const T& audio_data)
552         {
553                 auto sample_frame_count = static_cast<int>(audio_data.size()/format_desc_.audio_channels);
554
555                 audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));
556
557                 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))
558                         CASPAR_LOG(error) << print() << L" Failed to schedule audio.";
559
560                 audio_scheduled_ += sample_frame_count;
561         }
562                         
563         void schedule_next_video(core::const_frame frame)
564         {
565                 if (key_context_)
566                 {
567                         auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true));
568                         if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
569                                 CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
570                 }
571
572                 auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only));
573                 if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
574                         CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
575
576                 video_scheduled_ += format_desc_.duration;
577
578                 graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
579                 tick_timer_.restart();
580         }
581
582         std::future<bool> send(core::const_frame frame)
583         {
584                 auto exception = lock(exception_mutex_, [&]
585                 {
586                         return exception_;
587                 });
588
589                 if(exception != nullptr)
590                         std::rethrow_exception(exception);              
591
592                 if(!is_running_)
593                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Is not running."));
594                 
595                 bool audio_ready = !config_.embedded_audio;
596                 bool video_ready = false;
597
598                 auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
599                 {
600                         if (!audio_ready)
601                                 audio_ready = audio_frame_buffer_.try_push(frame);
602
603                         if (!video_ready)
604                                 video_ready = video_frame_buffer_.try_push(frame);
605
606                         if (audio_ready && video_ready)
607                                 return true;
608                         else
609                                 return boost::optional<bool>();
610                 };
611                 
612                 if (enqueue_task())
613                         return make_ready_future(true);
614
615                 send_completion_.set_task(enqueue_task);
616
617                 return send_completion_.get_future();
618         }
619         
620         std::wstring print() const
621         {
622                 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
623                         return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
624                                 boost::lexical_cast<std::wstring>(config_.device_index) +
625                                 L"&&" +
626                                 boost::lexical_cast<std::wstring>(config_.key_device_index()) +
627                                 L"|" +
628                                 format_desc_.name + L"]";
629                 else
630                         return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_)+L"-" +
631                                 boost::lexical_cast<std::wstring>(config_.device_index) + L"|" + format_desc_.name + L"]";
632         }
633 };
634
635 struct decklink_consumer_proxy : public core::frame_consumer
636 {
637         core::monitor::subject                          monitor_subject_;
638         const configuration                                     config_;
639         std::unique_ptr<decklink_consumer>      consumer_;
640         core::video_format_desc                         format_desc_;
641         executor                                                        executor_;
642 public:
643
644         decklink_consumer_proxy(const configuration& config)
645                 : config_(config)
646                 , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")
647         {
648                 auto ctx = core::diagnostics::call_context::for_thread();
649                 executor_.begin_invoke([=]
650                 {
651                         core::diagnostics::call_context::for_thread() = ctx;
652                         com_initialize();
653                 });
654         }
655
656         ~decklink_consumer_proxy()
657         {
658                 executor_.invoke([=]
659                 {
660                         consumer_.reset();
661                         com_uninitialize();
662                 });
663         }
664
665         // frame_consumer
666         
667         void initialize(const core::video_format_desc& format_desc, int channel_index) override
668         {
669                 format_desc_ = format_desc;
670                 executor_.invoke([=]
671                 {
672                         consumer_.reset();
673                         consumer_.reset(new decklink_consumer(config_, format_desc, channel_index));                    
674                 });
675         }
676         
677         std::future<bool> send(core::const_frame frame) override
678         {
679                 return consumer_->send(frame);
680         }
681         
682         std::wstring print() const override
683         {
684                 return consumer_ ? consumer_->print() : L"[decklink_consumer]";
685         }               
686
687         std::wstring name() const override
688         {
689                 return L"decklink";
690         }
691
692         boost::property_tree::wptree info() const override
693         {
694                 boost::property_tree::wptree info;
695                 info.add(L"type", L"decklink");
696                 info.add(L"key-only", config_.key_only);
697                 info.add(L"device", config_.device_index);
698
699                 if (config_.keyer == configuration::keyer_t::external_separate_device_keyer)
700                 {
701                         info.add(L"key-device", config_.key_device_index());
702                 }
703
704                 info.add(L"low-latency", config_.latency == configuration::latency_t::low_latency);
705                 info.add(L"embedded-audio", config_.embedded_audio);
706                 info.add(L"presentation-frame-age", presentation_frame_age_millis());
707                 //info.add(L"internal-key", config_.internal_key);
708                 return info;
709         }
710
711         int buffer_depth() const override
712         {
713                 return config_.buffer_depth() + 2;
714         }
715
716         int index() const override
717         {
718                 return 300 + config_.device_index;
719         }
720
721         int64_t presentation_frame_age_millis() const override
722         {
723                 return consumer_ ? static_cast<int64_t>(consumer_->current_presentation_delay_) : 0;
724         }
725
726         core::monitor::subject& monitor_output()
727         {
728                 return monitor_subject_;
729         }
730 };      
731
732 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
733 {
734         sink.short_description(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
735         sink.syntax(L"DECKLINK "
736                                 L"{[device_index:int]|1} "
737                                 L"{[keyer:INTERNAL_KEY,EXTERNAL_KEY,EXTERNAL_SEPARATE_DEVICE_KEY]} "
738                                 L"{[low_latency:LOW_LATENCY]} "
739                                 L"{[embedded_audio:EMBEDDED_AUDIO]} "
740                                 L"{[key_only:KEY_ONLY]}");
741         sink.para()->text(L"Sends video on an SDI output using Blackmagic Decklink video cards.");
742         sink.definitions()
743                 ->item(L"device_index", L"The Blackmagic video card to use (See Blackmagic control panel for card order). Default is 1.")
744                 ->item(L"keyer",
745                                 L"If given tries to enable either internal or external keying. Not all Blackmagic cards supports this. "
746                                 L"There is also a third experimental option (EXTERNAL_SEPARATE_DEVICE_KEY) which allocates device_index + 1 for synhronized key output.")
747                 ->item(L"low_latency", L"Tries to enable low latency if given.")
748                 ->item(L"embedded_audio", L"Embeds the audio into the SDI signal if given.")
749                 ->item(L"key_only",
750                                 L" will extract only the alpha channel from the "
751                                 L"channel. This is useful when you have two SDI video cards, and neither has native support "
752                                 L"for separate fill/key output");
753         sink.para()->text(L"Examples:");
754         sink.example(L">> ADD 1 DECKLINK", L"for using the default device_index of 1.");
755         sink.example(L">> ADD 1 DECKLINK 2", L"uses device_index 2.");
756         sink.example(L">> ADD 1 DECKLINK 1 EXTERNAL_KEY EMBEDDED_AUDIO");
757         sink.example(
758                         L">> ADD 1 DECKLINK 1 EMBEDDED_AUDIO\n"
759                         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.");
760         sink.example(
761                         L">> ADD 1 DECKLINK 1 EXTERNAL_SEPARATE_DEVICE_KEY EMBEDDED_AUDIO",
762                         L"Uses device 2 for key output. May give better sync between key and fill than the previous method.");
763 }
764
765 spl::shared_ptr<core::frame_consumer> create_consumer(
766                 const std::vector<std::wstring>& params, core::interaction_sink*)
767 {
768         if (params.size() < 1 || !boost::iequals(params.at(0), L"DECKLINK"))
769                 return core::frame_consumer::empty();
770         
771         configuration config;
772                 
773         if (params.size() > 1)
774                 config.device_index = boost::lexical_cast<int>(params.at(1));
775         
776         if (contains_param(L"INTERNAL_KEY", params))
777                 config.keyer = configuration::keyer_t::internal_keyer;
778         else if (contains_param(L"EXTERNAL_KEY", params))
779                 config.keyer = configuration::keyer_t::external_keyer;
780         else if (contains_param(L"EXTERNAL_SEPARATE_DEVICE_KEY", params))
781                 config.keyer = configuration::keyer_t::external_separate_device_keyer;
782         else
783                 config.keyer = configuration::keyer_t::default_keyer;
784
785         if (contains_param(L"LOW_LATENCY", params))
786                 config.latency = configuration::latency_t::low_latency;
787
788         config.embedded_audio   = contains_param(L"EMBEDDED_AUDIO", params);
789         config.key_only                 = contains_param(L"KEY_ONLY", params);
790
791         return spl::make_shared<decklink_consumer_proxy>(config);
792 }
793
794 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
795                 const boost::property_tree::wptree& ptree, core::interaction_sink*)
796 {
797         configuration config;
798
799         auto keyer = ptree.get(L"keyer", L"default");
800         if(keyer == L"external")
801                 config.keyer = configuration::keyer_t::external_keyer;
802         else if(keyer == L"internal")
803                 config.keyer = configuration::keyer_t::internal_keyer;
804         else if (keyer == L"external_separate_device")
805                 config.keyer = configuration::keyer_t::external_separate_device_keyer;
806
807         auto latency = ptree.get(L"latency", L"normal");
808         if(latency == L"low")
809                 config.latency = configuration::latency_t::low_latency;
810         else if(latency == L"normal")
811                 config.latency = configuration::latency_t::normal_latency;
812
813         config.key_only                         = ptree.get(L"key-only",                config.key_only);
814         config.device_index                     = ptree.get(L"device",                  config.device_index);
815         config.key_device_idx           = ptree.get(L"key-device",              config.key_device_idx);
816         config.embedded_audio           = ptree.get(L"embedded-audio",  config.embedded_audio);
817         config.base_buffer_depth        = ptree.get(L"buffer-depth",    config.base_buffer_depth);
818
819         return spl::make_shared<decklink_consumer_proxy>(config);
820 }
821
822 }}
823
824 /*
825 ##############################################################################
826 Pre-rolling
827
828 Mail: 2011-05-09
829
830 Yoshan
831 BMD Developer Support
832 developer@blackmagic-design.com
833
834 -----------------------------------------------------------------------------
835
836 Thanks for your inquiry. The minimum number of frames that you can preroll 
837 for scheduled playback is three frames for video and four frames for audio. 
838 As you mentioned if you preroll less frames then playback will not start or
839 playback will be very sporadic. From our experience with Media Express, we 
840 recommended that at least seven frames are prerolled for smooth playback. 
841
842 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:
843 There can be around 3 frames worth of latency on scheduled output.
844 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is
845 reduced  or removed for scheduled playback. If the DisplayVideoFrameSync() 
846 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will 
847 guarantee that the provided frame will be output as soon the previous 
848 frame output has been completed.
849 ################################################################################
850 */
851
852 /*
853 ##############################################################################
854 Async DMA Transfer without redundant copying
855
856 Mail: 2011-05-10
857
858 Yoshan
859 BMD Developer Support
860 developer@blackmagic-design.com
861
862 -----------------------------------------------------------------------------
863
864 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame 
865 and providing a pointer to your video buffer when GetBytes() is called. 
866 This may help to keep copying to a minimum. Please ensure that the pixel 
867 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will 
868 have to colourspace convert which may result in additional copying.
869 ################################################################################
870 */