]> git.sesse.net Git - casparcg/blob - modules/decklink/consumer/decklink_consumer.cpp
2.0.2: - Send index information to consumers.
[casparcg] / modules / decklink / consumer / decklink_consumer.cpp
1 /*\r
2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 *  This file is part of CasparCG.\r
5 *\r
6 *    CasparCG is free software: you can redistribute it and/or modify\r
7 *    it under the terms of the GNU General Public License as published by\r
8 *    the Free Software Foundation, either version 3 of the License, or\r
9 *    (at your option) any later version.\r
10 *\r
11 *    CasparCG is distributed in the hope that it will be useful,\r
12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 *    GNU General Public License for more details.\r
15 \r
16 *    You should have received a copy of the GNU General Public License\r
17 *    along with CasparCG.  If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 */\r
20 \r
21 #include "../StdAfx.h"\r
22  \r
23 #include "decklink_consumer.h"\r
24 \r
25 #include "../util/util.h"\r
26 \r
27 #include "../interop/DeckLinkAPI_h.h"\r
28 \r
29 #include <core/mixer/read_frame.h>\r
30 \r
31 #include <common/concurrency/com_context.h>\r
32 #include <common/diagnostics/graph.h>\r
33 #include <common/exception/exceptions.h>\r
34 #include <common/memory/memcpy.h>\r
35 #include <common/memory/memclr.h>\r
36 #include <common/memory/memshfl.h>\r
37 \r
38 #include <core/consumer/frame_consumer.h>\r
39 \r
40 #include <tbb/concurrent_queue.h>\r
41 #include <tbb/cache_aligned_allocator.h>\r
42 \r
43 #include <boost/circular_buffer.hpp>\r
44 #include <boost/timer.hpp>\r
45 \r
46 namespace caspar { namespace decklink { \r
47         \r
48 struct configuration\r
49 {\r
50         size_t  device_index;\r
51         bool    embedded_audio;\r
52         bool    internal_key;\r
53         bool    low_latency;\r
54         bool    key_only;\r
55         size_t  base_buffer_depth;\r
56         size_t  buffer_depth;\r
57         \r
58         configuration()\r
59                 : device_index(1)\r
60                 , embedded_audio(false)\r
61                 , internal_key(false)\r
62                 , low_latency(true)\r
63                 , key_only(false)\r
64                 , base_buffer_depth(3)\r
65                 , buffer_depth(base_buffer_depth + (low_latency ? 0 : 1) + (embedded_audio ? 1 : 0)){}\r
66 };\r
67 \r
68 class decklink_frame : public IDeckLinkVideoFrame\r
69 {\r
70         tbb::atomic<int>                                                                                        ref_count_;\r
71         std::shared_ptr<core::read_frame>                                                       frame_;\r
72         const core::video_format_desc                                                           format_desc_;\r
73 \r
74         bool                                                                                                            key_only_;\r
75         std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> key_data_;\r
76 public:\r
77         decklink_frame(const safe_ptr<core::read_frame>& frame, const core::video_format_desc& format_desc, bool key_only)\r
78                 : frame_(frame)\r
79                 , format_desc_(format_desc)\r
80                 , key_only_(key_only)\r
81         {\r
82                 ref_count_ = 0;\r
83         }\r
84         \r
85         STDMETHOD (QueryInterface(REFIID, LPVOID*))             {return E_NOINTERFACE;}\r
86         STDMETHOD_(ULONG,                       AddRef())                       \r
87         {\r
88                 return ++ref_count_;\r
89         }\r
90         STDMETHOD_(ULONG,                       Release())                      \r
91         {\r
92                 --ref_count_;\r
93                 if(ref_count_ == 0)\r
94                         delete this;\r
95                 return ref_count_;\r
96         }\r
97 \r
98         STDMETHOD_(long,                        GetWidth())                     {return format_desc_.width;}        \r
99     STDMETHOD_(long,                    GetHeight())            {return format_desc_.height;}        \r
100     STDMETHOD_(long,                    GetRowBytes())          {return format_desc_.width*4;}        \r
101         STDMETHOD_(BMDPixelFormat,      GetPixelFormat())       {return bmdFormat8BitBGRA;}        \r
102     STDMETHOD_(BMDFrameFlags,   GetFlags())                     {return bmdFrameFlagDefault;}\r
103         \r
104     STDMETHOD(GetBytes(void** buffer))\r
105         {\r
106                 static std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> zeros(1920*1080*4, 0);\r
107                 if(static_cast<size_t>(frame_->image_data().size()) != format_desc_.size)\r
108                 {\r
109                         *buffer = zeros.data();\r
110                         return S_OK;\r
111                 }\r
112 \r
113                 if(!key_only_)\r
114                         *buffer = const_cast<uint8_t*>(frame_->image_data().begin());\r
115                 else\r
116                 {\r
117                         if(key_data_.empty())\r
118                         {\r
119                                 key_data_.resize(frame_->image_data().size());\r
120                                 fast_memshfl(key_data_.data(), frame_->image_data().begin(), frame_->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
121                                 frame_.reset();\r
122                         }\r
123                         *buffer = key_data_.data();\r
124                 }\r
125 \r
126                 return S_OK;\r
127         }\r
128         \r
129     STDMETHOD(GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode)){return S_FALSE;}        \r
130     STDMETHOD(GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary))                 {return S_FALSE;}\r
131 };\r
132 \r
133 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable\r
134 {               \r
135         const configuration                                     config_;\r
136         const int                                                       channel_index_;\r
137         const int                                                       sub_index_;\r
138 \r
139         CComPtr<IDeckLink>                                      decklink_;\r
140         CComQIPtr<IDeckLinkOutput>                      output_;\r
141         CComQIPtr<IDeckLinkConfiguration>       configuration_;\r
142         CComQIPtr<IDeckLinkKeyer>                       keyer_;\r
143 \r
144         tbb::spin_mutex                                         exception_mutex_;\r
145         std::exception_ptr                                      exception_;\r
146 \r
147         tbb::atomic<bool>                                       is_running_;\r
148                 \r
149         const std::wstring                                      model_name_;\r
150         const core::video_format_desc           format_desc_;\r
151         const size_t                                            buffer_size_;\r
152 \r
153         long long                                                       frames_scheduled_;\r
154         long long                                                       audio_scheduled_;\r
155 \r
156         size_t                                                          preroll_count_;\r
157                 \r
158         boost::circular_buffer<std::vector<int32_t>>    audio_container_;\r
159 \r
160         tbb::concurrent_bounded_queue<std::shared_ptr<core::read_frame>> video_frame_buffer_;\r
161         tbb::concurrent_bounded_queue<std::shared_ptr<core::read_frame>> audio_frame_buffer_;\r
162         \r
163         safe_ptr<diagnostics::graph> graph_;\r
164         boost::timer tick_timer_;\r
165 \r
166 public:\r
167         decklink_consumer(const configuration& config, const core::video_format_desc& format_desc, int channel_index, int sub_index) \r
168                 : config_(config)\r
169                 , channel_index_(channel_index)\r
170                 , sub_index_(sub_index)\r
171                 , decklink_(get_device(config.device_index))\r
172                 , output_(decklink_)\r
173                 , configuration_(decklink_)\r
174                 , keyer_(decklink_)\r
175                 , model_name_(get_model_name(decklink_))\r
176                 , format_desc_(format_desc)\r
177                 , buffer_size_(config.buffer_depth) // Minimum buffer-size 3.\r
178                 , frames_scheduled_(0)\r
179                 , audio_scheduled_(0)\r
180                 , preroll_count_(0)\r
181                 , audio_container_(buffer_size_+1)\r
182         {\r
183                 is_running_ = true;\r
184                                 \r
185                 video_frame_buffer_.set_capacity(1);\r
186                 audio_frame_buffer_.set_capacity(1);\r
187 \r
188                 graph_->add_guide("tick-time", 0.5);\r
189                 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   \r
190                 graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));\r
191                 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));\r
192                 graph_->set_color("flushed-frame", diagnostics::color(0.4f, 0.3f, 0.8f));\r
193                 graph_->set_color("buffered-audio", diagnostics::color(0.9f, 0.9f, 0.5f));\r
194                 graph_->set_color("buffered-video", diagnostics::color(0.2f, 0.9f, 0.9f));\r
195                 graph_->set_text(print());\r
196                 diagnostics::register_graph(graph_);\r
197                 \r
198                 enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));\r
199                                 \r
200                 if(config.embedded_audio)\r
201                         enable_audio();\r
202 \r
203                 set_latency(config.low_latency);                                \r
204                 set_keyer(config.internal_key);\r
205                                 \r
206                 if(config.embedded_audio)               \r
207                         output_->BeginAudioPreroll();           \r
208                 \r
209                 for(size_t n = 0; n < buffer_size_; ++n)\r
210                         schedule_next_video(make_safe<core::read_frame>());\r
211 \r
212                 if(!config.embedded_audio)\r
213                         start_playback();\r
214         }\r
215 \r
216         ~decklink_consumer()\r
217         {               \r
218                 is_running_ = false;\r
219                 video_frame_buffer_.try_push(std::make_shared<core::read_frame>());\r
220                 audio_frame_buffer_.try_push(std::make_shared<core::read_frame>());\r
221 \r
222                 if(output_ != nullptr) \r
223                 {\r
224                         output_->StopScheduledPlayback(0, nullptr, 0);\r
225                         if(config_.embedded_audio)\r
226                                 output_->DisableAudioOutput();\r
227                         output_->DisableVideoOutput();\r
228                 }\r
229         }\r
230                         \r
231         void set_latency(bool low_latency)\r
232         {               \r
233                 if(!low_latency)\r
234                 {\r
235                         configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);\r
236                         CASPAR_LOG(info) << print() << L" Enabled normal-latency mode";\r
237                 }\r
238                 else\r
239                 {                       \r
240                         configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);\r
241                         CASPAR_LOG(info) << print() << L" Enabled low-latency mode";\r
242                 }\r
243         }\r
244 \r
245         void set_keyer(bool internal_key)\r
246         {\r
247                 if(internal_key) \r
248                 {\r
249                         if(FAILED(keyer_->Enable(FALSE)))                       \r
250                                 CASPAR_LOG(error) << print() << L" Failed to enable internal keyer.";                   \r
251                         else if(FAILED(keyer_->SetLevel(255)))                  \r
252                                 CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";\r
253                         else\r
254                                 CASPAR_LOG(info) << print() << L" Enabled internal keyer.";             \r
255                 }\r
256                 else\r
257                 {\r
258                         if(FAILED(keyer_->Enable(TRUE)))                        \r
259                                 CASPAR_LOG(error) << print() << L" Failed to enable external keyer.";   \r
260                         else if(FAILED(keyer_->SetLevel(255)))                  \r
261                                 CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";\r
262                         else\r
263                                 CASPAR_LOG(info) << print() << L" Enabled external keyer.";                     \r
264                 }\r
265         }\r
266         \r
267         void enable_audio()\r
268         {\r
269                 if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))\r
270                                 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable audio output."));\r
271                                 \r
272                 if(FAILED(output_->SetAudioCallback(this)))\r
273                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not set audio callback."));\r
274 \r
275                 CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";\r
276         }\r
277 \r
278         void enable_video(BMDDisplayMode display_mode)\r
279         {\r
280                 if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault))) \r
281                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable video output."));\r
282                 \r
283                 if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))\r
284                         BOOST_THROW_EXCEPTION(caspar_exception() \r
285                                                                         << msg_info(narrow(print()) + " Failed to set playback completion callback.")\r
286                                                                         << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));\r
287         }\r
288 \r
289         void start_playback()\r
290         {\r
291                 if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0))) \r
292                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback."));\r
293         }\r
294         \r
295         STDMETHOD (QueryInterface(REFIID, LPVOID*))     {return E_NOINTERFACE;}\r
296         STDMETHOD_(ULONG, AddRef())                                     {return 1;}\r
297         STDMETHOD_(ULONG, Release())                            {return 1;}\r
298         \r
299         STDMETHOD(ScheduledPlaybackHasStopped())\r
300         {\r
301                 is_running_ = false;\r
302                 CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";\r
303                 return S_OK;\r
304         }\r
305 \r
306         STDMETHOD(ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result))\r
307         {\r
308                 if(!is_running_)\r
309                         return E_FAIL;\r
310                 \r
311                 try\r
312                 {\r
313                         if(result == bmdOutputFrameDisplayedLate)\r
314                         {\r
315                                 graph_->add_tag("late-frame");\r
316                                 ++frames_scheduled_;\r
317                                 ++audio_scheduled_;\r
318                         }\r
319                         else if(result == bmdOutputFrameDropped)\r
320                                 graph_->add_tag("dropped-frame");\r
321                         else if(result == bmdOutputFrameFlushed)\r
322                                 graph_->add_tag("flushed-frame");\r
323 \r
324                         std::shared_ptr<core::read_frame> frame;        \r
325                         video_frame_buffer_.pop(frame);                                 \r
326                         schedule_next_video(make_safe_ptr(frame));      \r
327                         \r
328                         unsigned long buffered;\r
329                         output_->GetBufferedVideoFrameCount(&buffered);\r
330                         graph_->update_value("buffered-video", static_cast<double>(buffered)/format_desc_.fps);\r
331                 }\r
332                 catch(...)\r
333                 {\r
334                         tbb::spin_mutex::scoped_lock lock(exception_mutex_);\r
335                         exception_ = std::current_exception();\r
336                         return E_FAIL;\r
337                 }\r
338 \r
339                 return S_OK;\r
340         }\r
341                 \r
342         STDMETHOD(RenderAudioSamples(BOOL preroll))\r
343         {\r
344                 if(!is_running_)\r
345                         return E_FAIL;\r
346                 \r
347                 try\r
348                 {       \r
349                         if(preroll)\r
350                         {\r
351                                 if(++preroll_count_ >= buffer_size_)\r
352                                 {\r
353                                         output_->EndAudioPreroll();\r
354                                         start_playback();                               \r
355                                 }\r
356                                 else\r
357                                         schedule_next_audio(make_safe<core::read_frame>());     \r
358                         }\r
359                         else\r
360                         {\r
361                                 std::shared_ptr<core::read_frame> frame;\r
362                                 audio_frame_buffer_.pop(frame);\r
363                                 schedule_next_audio(make_safe_ptr(frame));      \r
364                         }\r
365 \r
366                         unsigned long buffered;\r
367                         output_->GetBufferedAudioSampleFrameCount(&buffered);\r
368                         graph_->update_value("buffered-audio", static_cast<double>(buffered)/(format_desc_.audio_samples_per_frame*2));\r
369                 }\r
370                 catch(...)\r
371                 {\r
372                         tbb::spin_mutex::scoped_lock lock(exception_mutex_);\r
373                         exception_ = std::current_exception();\r
374                         return E_FAIL;\r
375                 }\r
376 \r
377                 return S_OK;\r
378         }\r
379 \r
380         void schedule_next_audio(const safe_ptr<core::read_frame>& frame)\r
381         {\r
382                 const int sample_frame_count = frame->audio_data().size()/format_desc_.audio_channels;\r
383 \r
384                 audio_container_.push_back(std::vector<int32_t>(frame->audio_data().begin(), frame->audio_data().end()));\r
385 \r
386                 if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, (audio_scheduled_++) * sample_frame_count, format_desc_.audio_sample_rate, nullptr)))\r
387                         CASPAR_LOG(error) << print() << L" Failed to schedule audio.";\r
388         }\r
389                         \r
390         void schedule_next_video(const safe_ptr<core::read_frame>& frame)\r
391         {\r
392                 CComPtr<IDeckLinkVideoFrame> frame2(new decklink_frame(frame, format_desc_, config_.key_only));\r
393                 if(FAILED(output_->ScheduleVideoFrame(frame2, (frames_scheduled_++) * format_desc_.duration, format_desc_.duration, format_desc_.time_scale)))\r
394                         CASPAR_LOG(error) << print() << L" Failed to schedule video.";\r
395 \r
396                 graph_->update_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);\r
397                 tick_timer_.restart();\r
398         }\r
399 \r
400         void send(const safe_ptr<core::read_frame>& frame)\r
401         {\r
402                 {\r
403                         tbb::spin_mutex::scoped_lock lock(exception_mutex_);\r
404                         if(exception_ != nullptr)\r
405                                 std::rethrow_exception(exception_);\r
406                 }\r
407 \r
408                 if(!is_running_)\r
409                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Is not running."));\r
410                 \r
411                 if(config_.embedded_audio)\r
412                         audio_frame_buffer_.push(frame);        \r
413                 video_frame_buffer_.push(frame);        \r
414         }\r
415         \r
416         std::wstring print() const\r
417         {\r
418                 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_) + L"-" + boost::lexical_cast<std::wstring>(sub_index_) + L"|device " +\r
419                         boost::lexical_cast<std::wstring>(config_.device_index) + L"|" +  format_desc_.name + L"]";\r
420         }\r
421 };\r
422 \r
423 struct decklink_consumer_proxy : public core::frame_consumer\r
424 {\r
425         const configuration                             config_;\r
426         com_context<decklink_consumer>  context_;\r
427 public:\r
428 \r
429         decklink_consumer_proxy(const configuration& config)\r
430                 : config_(config)\r
431                 , context_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")\r
432         {\r
433         }\r
434 \r
435         ~decklink_consumer_proxy()\r
436         {\r
437                 auto str = print();\r
438                 context_.reset();\r
439                 CASPAR_LOG(info) << str << L" Successfully Uninitialized.";     \r
440         }\r
441         \r
442         virtual void initialize(const core::video_format_desc& format_desc, int channel_index, int sub_index)\r
443         {\r
444                 context_.reset([&]{return new decklink_consumer(config_, format_desc, channel_index, sub_index);});             \r
445                                 \r
446                 CASPAR_LOG(info) << print() << L" Successfully Initialized.";   \r
447         }\r
448         \r
449         virtual bool send(const safe_ptr<core::read_frame>& frame)\r
450         {\r
451                 context_->send(frame);\r
452                 return true;\r
453         }\r
454         \r
455         virtual std::wstring print() const\r
456         {\r
457                 return context_->print();\r
458         }                       \r
459 \r
460         virtual size_t buffer_depth() const\r
461         {\r
462                 return config_.buffer_depth;\r
463         }\r
464 };      \r
465 \r
466 safe_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>& params) \r
467 {\r
468         if(params.size() < 1 || params[0] != L"DECKLINK")\r
469                 return core::frame_consumer::empty();\r
470         \r
471         configuration config;\r
472                 \r
473         if(params.size() > 1)\r
474                 config.device_index = lexical_cast_or_default<int>(params[1], config.device_index);\r
475         \r
476         config.internal_key             = std::find(params.begin(), params.end(), L"INTERNAL_KEY")       != params.end();\r
477         config.low_latency              = std::find(params.begin(), params.end(), L"LOW_LATENCY")        != params.end();\r
478         config.embedded_audio   = std::find(params.begin(), params.end(), L"EMBEDDED_AUDIO") != params.end();\r
479         config.key_only                 = std::find(params.begin(), params.end(), L"KEY_ONLY")           != params.end();\r
480 \r
481         return make_safe<decklink_consumer_proxy>(config);\r
482 }\r
483 \r
484 safe_ptr<core::frame_consumer> create_consumer(const boost::property_tree::ptree& ptree) \r
485 {\r
486         configuration config;\r
487 \r
488         config.internal_key                     = ptree.get("internal-key",             config.internal_key);\r
489         config.low_latency                      = ptree.get("low-latency",              config.low_latency);\r
490         config.key_only                         = ptree.get("key-only",                 config.key_only);\r
491         config.device_index                     = ptree.get("device",                   config.device_index);\r
492         config.embedded_audio           = ptree.get("embedded-audio",   config.embedded_audio);\r
493         config.base_buffer_depth        = ptree.get("buffer-depth",             config.base_buffer_depth);\r
494 \r
495         return make_safe<decklink_consumer_proxy>(config);\r
496 }\r
497 \r
498 }}\r
499 \r
500 /*\r
501 ##############################################################################\r
502 Pre-rolling\r
503 \r
504 Mail: 2011-05-09\r
505 \r
506 Yoshan\r
507 BMD Developer Support\r
508 developer@blackmagic-design.com\r
509 \r
510 -----------------------------------------------------------------------------\r
511 \r
512 Thanks for your inquiry. The minimum number of frames that you can preroll \r
513 for scheduled playback is three frames for video and four frames for audio. \r
514 As you mentioned if you preroll less frames then playback will not start or\r
515 playback will be very sporadic. From our experience with Media Express, we \r
516 recommended that at least seven frames are prerolled for smooth playback. \r
517 \r
518 Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:\r
519 There can be around 3 frames worth of latency on scheduled output.\r
520 When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is\r
521 reduced  or removed for scheduled playback. If the DisplayVideoFrameSync() \r
522 method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will \r
523 guarantee that the provided frame will be output as soon the previous \r
524 frame output has been completed.\r
525 ################################################################################\r
526 */\r
527 \r
528 /*\r
529 ##############################################################################\r
530 Async DMA Transfer without redundant copying\r
531 \r
532 Mail: 2011-05-10\r
533 \r
534 Yoshan\r
535 BMD Developer Support\r
536 developer@blackmagic-design.com\r
537 \r
538 -----------------------------------------------------------------------------\r
539 \r
540 Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame \r
541 and providing a pointer to your video buffer when GetBytes() is called. \r
542 This may help to keep copying to a minimum. Please ensure that the pixel \r
543 format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will \r
544 have to colourspace convert which may result in additional copying.\r
545 ################################################################################\r
546 */