]> git.sesse.net Git - casparcg/blob - modules/decklink/util/util.h
d4abe5cb66965188080b1fd1dcdfa0ef94aa996c
[casparcg] / modules / decklink / util / util.h
1 /*\r
2 * Copyright 2013 Sveriges Television AB http://casparcg.com/\r
3 *\r
4 * This file is part of CasparCG (www.casparcg.com).\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 * Author: Robert Nagy, ronag89@gmail.com\r
20 */\r
21 \r
22 #pragma once\r
23 \r
24 #include <common/exception/exceptions.h>\r
25 #include <common/log/log.h>\r
26 #include <common/memory/memshfl.h>\r
27 #include <core/video_format.h>\r
28 #include <core/mixer/read_frame.h>\r
29 \r
30 #include "../interop/DeckLinkAPI_h.h"\r
31 \r
32 #include <boost/lexical_cast.hpp>\r
33 \r
34 #include <atlbase.h>\r
35 \r
36 #include <string>\r
37 \r
38 namespace caspar { namespace decklink {\r
39         \r
40 static BMDDisplayMode get_decklink_video_format(core::video_format::type fmt) \r
41 {\r
42         switch(fmt)\r
43         {\r
44         case core::video_format::pal:                   return bmdModePAL;\r
45         case core::video_format::ntsc:                  return bmdModeNTSC;\r
46         case core::video_format::x576p2500:             return (BMDDisplayMode)ULONG_MAX;\r
47         case core::video_format::x720p2398:             return (BMDDisplayMode)ULONG_MAX;\r
48         case core::video_format::x720p2400:             return (BMDDisplayMode)ULONG_MAX;\r
49         case core::video_format::x720p2500:             return (BMDDisplayMode)ULONG_MAX;\r
50         case core::video_format::x720p5000:             return bmdModeHD720p50;\r
51         case core::video_format::x720p2997:             return (BMDDisplayMode)ULONG_MAX;\r
52         case core::video_format::x720p5994:             return bmdModeHD720p5994;\r
53         case core::video_format::x720p3000:             return (BMDDisplayMode)ULONG_MAX;\r
54         case core::video_format::x720p6000:             return bmdModeHD720p60;\r
55         case core::video_format::x1080p2398:    return bmdModeHD1080p2398;\r
56         case core::video_format::x1080p2400:    return bmdModeHD1080p24;\r
57         case core::video_format::x1080i5000:    return bmdModeHD1080i50;\r
58         case core::video_format::x1080i5994:    return bmdModeHD1080i5994;\r
59         case core::video_format::x1080i6000:    return bmdModeHD1080i6000;\r
60         case core::video_format::x1080p2500:    return bmdModeHD1080p25;\r
61         case core::video_format::x1080p2997:    return bmdModeHD1080p2997;\r
62         case core::video_format::x1080p3000:    return bmdModeHD1080p30;\r
63         case core::video_format::x1080p5000:    return bmdModeHD1080p50;\r
64         case core::video_format::x1080p5994:    return bmdModeHD1080p5994;\r
65         case core::video_format::x1080p6000:    return bmdModeHD1080p6000;\r
66         case core::video_format::x1556p2398:    return bmdMode2k2398;\r
67         case core::video_format::x1556p2400:    return bmdMode2k24;\r
68         case core::video_format::x1556p2500:    return bmdMode2k25;\r
69         case core::video_format::x2160p2398:    return bmdMode4K2160p2398;\r
70         case core::video_format::x2160p2400:    return bmdMode4K2160p24;\r
71         case core::video_format::x2160p2500:    return bmdMode4K2160p25;\r
72         case core::video_format::x2160p2997:    return bmdMode4K2160p2997;\r
73         case core::video_format::x2160p3000:    return bmdMode4K2160p30;\r
74         default:                                                                return (BMDDisplayMode)ULONG_MAX;\r
75         }\r
76 }\r
77 \r
78 static core::video_format::type get_caspar_video_format(BMDDisplayMode fmt) \r
79 {\r
80         switch(fmt)\r
81         {\r
82         case bmdModePAL:                                                return core::video_format::pal;         \r
83         case bmdModeNTSC:                                               return core::video_format::ntsc;                \r
84         case bmdModeHD720p50:                                   return core::video_format::x720p5000;   \r
85         case bmdModeHD720p5994:                                 return core::video_format::x720p5994;   \r
86         case bmdModeHD720p60:                                   return core::video_format::x720p6000;   \r
87         case bmdModeHD1080p2398:                                return core::video_format::x1080p2398;  \r
88         case bmdModeHD1080p24:                                  return core::video_format::x1080p2400;  \r
89         case bmdModeHD1080i50:                                  return core::video_format::x1080i5000;  \r
90         case bmdModeHD1080i5994:                                return core::video_format::x1080i5994;  \r
91         case bmdModeHD1080i6000:                                return core::video_format::x1080i6000;  \r
92         case bmdModeHD1080p25:                                  return core::video_format::x1080p2500;  \r
93         case bmdModeHD1080p2997:                                return core::video_format::x1080p2997;  \r
94         case bmdModeHD1080p30:                                  return core::video_format::x1080p3000;  \r
95         case bmdModeHD1080p50:                                  return core::video_format::x1080p5000;  \r
96         case bmdModeHD1080p5994:                                return core::video_format::x1080p5994;  \r
97         case bmdModeHD1080p6000:                                return core::video_format::x1080p6000;  \r
98         case bmdMode2k2398:                                             return core::video_format::x1556p2398;  \r
99         case bmdMode2k24:                                               return core::video_format::x1556p2400;  \r
100         case bmdMode2k25:                                               return core::video_format::x1556p2500;  \r
101         case bmdMode4K2160p2398:                                return core::video_format::x2160p2398;  \r
102         case bmdMode4K2160p24:                                  return core::video_format::x2160p2400;  \r
103         case bmdMode4K2160p25:                                  return core::video_format::x2160p2500;  \r
104         case bmdMode4K2160p2997:                                return core::video_format::x2160p2997;  \r
105         case bmdMode4K2160p30:                                  return core::video_format::x2160p3000;  \r
106         default:                                                                return core::video_format::invalid;     \r
107         }\r
108 }\r
109 \r
110 template<typename T, typename F>\r
111 BMDDisplayMode get_display_mode(const T& device, BMDDisplayMode format, BMDPixelFormat pix_fmt, F flag)\r
112 {\r
113         CComPtr<IDeckLinkDisplayModeIterator> iterator;\r
114         CComPtr<IDeckLinkDisplayMode>             mode;\r
115         \r
116         if(SUCCEEDED(device->GetDisplayModeIterator(&iterator)))\r
117         {\r
118                 while(SUCCEEDED(iterator->Next(&mode)) && \r
119                                 mode != nullptr && \r
120                                 mode->GetDisplayMode() != format){}\r
121         }\r
122 \r
123         if(!mode)\r
124                 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Device could not find requested video-format.") \r
125                                                                                                  << arg_value_info(boost::lexical_cast<std::string>(format))\r
126                                                                                                  << arg_name_info("format"));\r
127                 \r
128         BMDDisplayModeSupport displayModeSupport;\r
129         if(FAILED(device->DoesSupportVideoMode(mode->GetDisplayMode(), pix_fmt, flag, &displayModeSupport, nullptr)) || displayModeSupport == bmdDisplayModeNotSupported)\r
130                 CASPAR_LOG(warning) << L"Device does not support video-format: " << mode->GetDisplayMode();\r
131                 //BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Device does not support requested video-format.")\r
132                 //                                                                               << arg_value_info(boost::lexical_cast<std::string>(format))\r
133                 //                                                                               << arg_name_info("format"));\r
134         else if(displayModeSupport == bmdDisplayModeSupportedWithConversion)\r
135                 CASPAR_LOG(warning) << L"Device supports video-format with conversion: " << mode->GetDisplayMode();\r
136 \r
137         return mode->GetDisplayMode();\r
138 }\r
139 \r
140 template<typename T, typename F>\r
141 static BMDDisplayMode get_display_mode(const T& device, core::video_format::type fmt, BMDPixelFormat pix_fmt, F flag)\r
142 {       \r
143         return get_display_mode(device, get_decklink_video_format(fmt), pix_fmt, flag);\r
144 }\r
145 \r
146 template<typename T>\r
147 static std::wstring get_version(T& iterator)\r
148 {\r
149         CComQIPtr<IDeckLinkAPIInformation> info = iterator;\r
150         if (!info)\r
151                 return L"Unknown";\r
152         \r
153         BSTR ver;               \r
154         info->GetString(BMDDeckLinkAPIVersion, &ver);\r
155                 \r
156         return ver;                                     \r
157 }\r
158 \r
159 static CComPtr<IDeckLink> get_device(size_t device_index)\r
160 {\r
161         CComPtr<IDeckLinkIterator> pDecklinkIterator;\r
162         if(FAILED(pDecklinkIterator.CoCreateInstance(CLSID_CDeckLinkIterator)))\r
163                 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Decklink drivers not found."));\r
164                 \r
165         size_t n = 0;\r
166         CComPtr<IDeckLink> decklink;\r
167         while(n < device_index && pDecklinkIterator->Next(&decklink) == S_OK){++n;}     \r
168 \r
169         if(n != device_index || !decklink)\r
170                 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Decklink device not found.") << arg_name_info("device_index") << arg_value_info(boost::lexical_cast<std::string>(device_index)));\r
171                 \r
172         return decklink;\r
173 }\r
174 \r
175 template <typename T>\r
176 static std::wstring get_model_name(const T& device)\r
177 {       \r
178         BSTR pModelName;\r
179         device->GetModelName(&pModelName);\r
180         return std::wstring(pModelName);\r
181 }\r
182 \r
183 static std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> extract_key(\r
184                 const safe_ptr<core::read_frame>& frame)\r
185 {\r
186         std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> result;\r
187 \r
188         result.resize(frame->image_data().size());\r
189         fast_memshfl(\r
190                         result.data(),\r
191                         frame->image_data().begin(),\r
192                         frame->image_data().size(),\r
193                         0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
194 \r
195         return std::move(result);\r
196 }\r
197 \r
198 class decklink_frame : public IDeckLinkVideoFrame\r
199 {\r
200         tbb::atomic<int>                                                                                        ref_count_;\r
201         std::shared_ptr<core::read_frame>                                                       frame_;\r
202         const core::video_format_desc                                                           format_desc_;\r
203 \r
204         const bool                                                                                                      key_only_;\r
205         std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> data_;\r
206 public:\r
207         decklink_frame(const safe_ptr<core::read_frame>& frame, const core::video_format_desc& format_desc, bool key_only)\r
208                 : frame_(frame)\r
209                 , format_desc_(format_desc)\r
210                 , key_only_(key_only)\r
211         {\r
212                 ref_count_ = 0;\r
213         }\r
214 \r
215         decklink_frame(const safe_ptr<core::read_frame>& frame, const core::video_format_desc& format_desc, std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>>&& key_data)\r
216                 : frame_(frame)\r
217                 , format_desc_(format_desc)\r
218                 , key_only_(true)\r
219                 , data_(std::move(key_data))\r
220         {\r
221                 ref_count_ = 0;\r
222         }\r
223         \r
224         // IUnknown\r
225 \r
226         STDMETHOD (QueryInterface(REFIID, LPVOID*))             \r
227         {\r
228                 return E_NOINTERFACE;\r
229         }\r
230         \r
231         STDMETHOD_(ULONG,                       AddRef())                       \r
232         {\r
233                 return ++ref_count_;\r
234         }\r
235 \r
236         STDMETHOD_(ULONG,                       Release())                      \r
237         {\r
238                 if(--ref_count_ == 0)\r
239                         delete this;\r
240                 return ref_count_;\r
241         }\r
242 \r
243         // IDecklinkVideoFrame\r
244 \r
245         STDMETHOD_(long,                        GetWidth())                     {return format_desc_.width;}        \r
246     STDMETHOD_(long,                    GetHeight())            {return format_desc_.height;}        \r
247     STDMETHOD_(long,                    GetRowBytes())          {return format_desc_.width*4;}        \r
248         STDMETHOD_(BMDPixelFormat,      GetPixelFormat())       {return bmdFormat8BitBGRA;}        \r
249     STDMETHOD_(BMDFrameFlags,   GetFlags())                     {return bmdFrameFlagDefault;}\r
250         \r
251     STDMETHOD(GetBytes(void** buffer))\r
252         {\r
253                 try\r
254                 {\r
255                         if(static_cast<size_t>(frame_->image_data().size()) != format_desc_.size)\r
256                         {\r
257                                 data_.resize(format_desc_.size, 0);\r
258                                 *buffer = data_.data();\r
259                         }\r
260                         else if(key_only_)\r
261                         {\r
262                                 if(data_.empty())\r
263                                 {\r
264                                         data_.resize(frame_->image_data().size());\r
265                                         fast_memshfl(data_.data(), frame_->image_data().begin(), frame_->image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);\r
266                                 }\r
267                                 *buffer = data_.data();\r
268                         }\r
269                         else\r
270                                 *buffer = const_cast<uint8_t*>(frame_->image_data().begin());\r
271                 }\r
272                 catch(...)\r
273                 {\r
274                         CASPAR_LOG_CURRENT_EXCEPTION();\r
275                         return E_FAIL;\r
276                 }\r
277 \r
278                 return S_OK;\r
279         }\r
280         \r
281     STDMETHOD(GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode)){return S_FALSE;}        \r
282     STDMETHOD(GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary))                 {return S_FALSE;}\r
283 \r
284         // decklink_frame       \r
285 \r
286         const boost::iterator_range<const int32_t*> audio_data()\r
287         {\r
288                 return frame_->audio_data();\r
289         }\r
290 \r
291         int64_t get_age_millis() const\r
292         {\r
293                 return frame_->get_age_millis();\r
294         }\r
295 };\r
296 \r
297 struct configuration\r
298 {\r
299         enum keyer_t\r
300         {\r
301                 internal_keyer,\r
302                 external_keyer,\r
303                 default_keyer\r
304         };\r
305 \r
306         enum latency_t\r
307         {\r
308                 low_latency,\r
309                 normal_latency,\r
310                 default_latency\r
311         };\r
312 \r
313         size_t                                  device_index;\r
314         bool                                    embedded_audio;\r
315         core::channel_layout    audio_layout;\r
316         keyer_t                                 keyer;\r
317         latency_t                               latency;\r
318         bool                                    key_only;\r
319         size_t                                  base_buffer_depth;\r
320         bool                                    custom_allocator;\r
321         \r
322         configuration()\r
323                 : device_index(1)\r
324                 , embedded_audio(false)\r
325                 , audio_layout(core::default_channel_layout_repository().get_by_name(L"STEREO"))\r
326                 , keyer(default_keyer)\r
327                 , latency(default_latency)\r
328                 , key_only(false)\r
329                 , base_buffer_depth(3)\r
330                 , custom_allocator(true)\r
331         {\r
332         }\r
333         \r
334         size_t buffer_depth() const\r
335         {\r
336                 return base_buffer_depth + (latency == low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);\r
337         }\r
338 \r
339         int num_out_channels() const\r
340         {\r
341                 if (audio_layout.num_channels <= 2)\r
342                         return 2;\r
343                 \r
344                 if (audio_layout.num_channels <= 8)\r
345                         return 8;\r
346 \r
347                 return 16;\r
348         }\r
349 };\r
350 \r
351 static void set_latency(\r
352                 const CComQIPtr<IDeckLinkConfiguration>& config,\r
353                 configuration::latency_t latency,\r
354                 const std::wstring& print)\r
355 {               \r
356         if (latency == configuration::low_latency)\r
357         {\r
358                 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);\r
359                 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";\r
360         }\r
361         else if (latency == configuration::normal_latency)\r
362         {                       \r
363                 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);\r
364                 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";\r
365         }\r
366 }\r
367 \r
368 static void set_keyer(\r
369                 const CComQIPtr<IDeckLinkAttributes>& attributes,\r
370                 const CComQIPtr<IDeckLinkKeyer>& decklink_keyer,\r
371                 configuration::keyer_t keyer,\r
372                 const std::wstring& print)\r
373 {\r
374         if (keyer == configuration::internal_keyer) \r
375         {\r
376                 BOOL value = true;\r
377                 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)\r
378                         CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";     \r
379                 else if (FAILED(decklink_keyer->Enable(FALSE)))                 \r
380                         CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";                     \r
381                 else if (FAILED(decklink_keyer->SetLevel(255)))                 \r
382                         CASPAR_LOG(error) << print << L" Failed to set key-level to max.";\r
383                 else\r
384                         CASPAR_LOG(info) << print << L" Enabled internal keyer.";               \r
385         }\r
386         else if (keyer == configuration::external_keyer)\r
387         {\r
388                 BOOL value = true;\r
389                 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)\r
390                         CASPAR_LOG(error) << print << L" Failed to enable external keyer.";     \r
391                 else if (FAILED(decklink_keyer->Enable(TRUE)))                  \r
392                         CASPAR_LOG(error) << print << L" Failed to enable external keyer.";     \r
393                 else if (FAILED(decklink_keyer->SetLevel(255)))                 \r
394                         CASPAR_LOG(error) << print << L" Failed to set key-level to max.";\r
395                 else\r
396                         CASPAR_LOG(info) << print << L" Enabled external keyer.";                       \r
397         }\r
398 }\r
399 \r
400 class reference_signal_detector\r
401 {\r
402         CComQIPtr<IDeckLinkOutput> output_;\r
403         BMDReferenceStatus last_reference_status_;\r
404 public:\r
405         reference_signal_detector(const CComQIPtr<IDeckLinkOutput>& output)\r
406                 : output_(output)\r
407                 , last_reference_status_(static_cast<BMDReferenceStatus>(-1))\r
408         {\r
409         }\r
410 \r
411         template<typename Print>\r
412         void detect_change(const Print& print)\r
413         {\r
414                 BMDReferenceStatus reference_status;\r
415 \r
416                 if (output_->GetReferenceStatus(&reference_status) != S_OK)\r
417                 {\r
418                         CASPAR_LOG(error) << print() << L" Reference signal: failed while querying status";\r
419                 }\r
420                 else if (reference_status != last_reference_status_)\r
421                 {\r
422                         last_reference_status_ = reference_status;\r
423 \r
424                         if (reference_status == 0)\r
425                                 CASPAR_LOG(info) << print() << L" Reference signal: not detected.";\r
426                         else if (reference_status & bmdReferenceNotSupportedByHardware)\r
427                                 CASPAR_LOG(info) << print() << L" Reference signal: not supported by hardware.";\r
428                         else if (reference_status & bmdReferenceLocked)\r
429                                 CASPAR_LOG(info) << print() << L" Reference signal: locked.";\r
430                         else\r
431                                 CASPAR_LOG(info) << print() << L" Reference signal: Unhandled enum bitfield: " << reference_status;\r
432                 }\r
433         }\r
434 };\r
435 \r
436 }}