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