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