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