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