2 * Copyright 2013 Sveriges Television AB http://casparcg.com/
\r
4 * This file is part of CasparCG (www.casparcg.com).
\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
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
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
19 * Author: Robert Nagy, ronag89@gmail.com
\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
30 #include "../interop/DeckLinkAPI_h.h"
\r
32 #include <boost/lexical_cast.hpp>
\r
34 #include <atlbase.h>
\r
38 namespace caspar { namespace decklink {
\r
40 static BMDDisplayMode get_decklink_video_format(core::video_format::type fmt)
\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
78 static core::video_format::type get_caspar_video_format(BMDDisplayMode fmt)
\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
110 template<typename T, typename F>
\r
111 BMDDisplayMode get_display_mode(const T& device, BMDDisplayMode format, BMDPixelFormat pix_fmt, F flag)
\r
113 CComPtr<IDeckLinkDisplayModeIterator> iterator;
\r
114 CComPtr<IDeckLinkDisplayMode> mode;
\r
116 if(SUCCEEDED(device->GetDisplayModeIterator(&iterator)))
\r
118 while(SUCCEEDED(iterator->Next(&mode)) &&
\r
119 mode != nullptr &&
\r
120 mode->GetDisplayMode() != format){}
\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
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
137 return mode->GetDisplayMode();
\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
143 return get_display_mode(device, get_decklink_video_format(fmt), pix_fmt, flag);
\r
146 template<typename T>
\r
147 static std::wstring get_version(T& iterator)
\r
149 CComQIPtr<IDeckLinkAPIInformation> info = iterator;
\r
154 info->GetString(BMDDeckLinkAPIVersion, &ver);
\r
159 static CComPtr<IDeckLink> get_device(size_t device_index)
\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
166 CComPtr<IDeckLink> decklink;
\r
167 while(n < device_index && pDecklinkIterator->Next(&decklink) == S_OK){++n;}
\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
175 template <typename T>
\r
176 static std::wstring get_model_name(const T& device)
\r
179 device->GetModelName(&pModelName);
\r
180 return std::wstring(pModelName);
\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
186 std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> result;
\r
188 result.resize(frame->image_data().size());
\r
191 frame->image_data().begin(),
\r
192 frame->image_data().size(),
\r
193 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
\r
195 return std::move(result);
\r
198 class decklink_frame : public IDeckLinkVideoFrame
\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
204 const bool key_only_;
\r
205 std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>> data_;
\r
207 decklink_frame(const safe_ptr<core::read_frame>& frame, const core::video_format_desc& format_desc, bool key_only)
\r
209 , format_desc_(format_desc)
\r
210 , key_only_(key_only)
\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
217 , format_desc_(format_desc)
\r
219 , data_(std::move(key_data))
\r
226 STDMETHOD (QueryInterface(REFIID, LPVOID*))
\r
228 return E_NOINTERFACE;
\r
231 STDMETHOD_(ULONG, AddRef())
\r
233 return ++ref_count_;
\r
236 STDMETHOD_(ULONG, Release())
\r
238 if(--ref_count_ == 0)
\r
243 // IDecklinkVideoFrame
\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
251 STDMETHOD(GetBytes(void** buffer))
\r
255 if(static_cast<size_t>(frame_->image_data().size()) != format_desc_.size)
\r
257 data_.resize(format_desc_.size, 0);
\r
258 *buffer = data_.data();
\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
267 *buffer = data_.data();
\r
270 *buffer = const_cast<uint8_t*>(frame_->image_data().begin());
\r
274 CASPAR_LOG_CURRENT_EXCEPTION();
\r
281 STDMETHOD(GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode)){return S_FALSE;}
\r
282 STDMETHOD(GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary)) {return S_FALSE;}
\r
286 const boost::iterator_range<const int32_t*> audio_data()
\r
288 return frame_->audio_data();
\r
291 int64_t get_age_millis() const
\r
293 return frame_->get_age_millis();
\r
297 struct configuration
\r
313 size_t device_index;
\r
314 bool embedded_audio;
\r
315 core::channel_layout audio_layout;
\r
319 size_t base_buffer_depth;
\r
323 , embedded_audio(false)
\r
324 , audio_layout(core::default_channel_layout_repository().get_by_name(L"STEREO"))
\r
325 , keyer(default_keyer)
\r
326 , latency(default_latency)
\r
328 , base_buffer_depth(3)
\r
332 size_t buffer_depth() const
\r
334 return base_buffer_depth + (latency == low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
\r
337 int num_out_channels() const
\r
339 if (audio_layout.num_channels <= 2)
\r
342 if (audio_layout.num_channels <= 8)
\r
349 static void set_latency(
\r
350 const CComQIPtr<IDeckLinkConfiguration>& config,
\r
351 configuration::latency_t latency,
\r
352 const std::wstring& print)
\r
354 if (latency == configuration::low_latency)
\r
356 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);
\r
357 CASPAR_LOG(info) << print << L" Enabled low-latency mode.";
\r
359 else if (latency == configuration::normal_latency)
\r
361 config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);
\r
362 CASPAR_LOG(info) << print << L" Disabled low-latency mode.";
\r
366 static void set_keyer(
\r
367 const CComQIPtr<IDeckLinkAttributes>& attributes,
\r
368 const CComQIPtr<IDeckLinkKeyer>& decklink_keyer,
\r
369 configuration::keyer_t keyer,
\r
370 const std::wstring& print)
\r
372 if (keyer == configuration::internal_keyer)
\r
375 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &value)) && !value)
\r
376 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
\r
377 else if (FAILED(decklink_keyer->Enable(FALSE)))
\r
378 CASPAR_LOG(error) << print << L" Failed to enable internal keyer.";
\r
379 else if (FAILED(decklink_keyer->SetLevel(255)))
\r
380 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
\r
382 CASPAR_LOG(info) << print << L" Enabled internal keyer.";
\r
384 else if (keyer == configuration::external_keyer)
\r
387 if (SUCCEEDED(attributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &value)) && !value)
\r
388 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
\r
389 else if (FAILED(decklink_keyer->Enable(TRUE)))
\r
390 CASPAR_LOG(error) << print << L" Failed to enable external keyer.";
\r
391 else if (FAILED(decklink_keyer->SetLevel(255)))
\r
392 CASPAR_LOG(error) << print << L" Failed to set key-level to max.";
\r
394 CASPAR_LOG(info) << print << L" Enabled external keyer.";
\r
398 class reference_signal_detector
\r
400 CComQIPtr<IDeckLinkOutput> output_;
\r
401 BMDReferenceStatus last_reference_status_;
\r
403 reference_signal_detector(const CComQIPtr<IDeckLinkOutput>& output)
\r
405 , last_reference_status_(static_cast<BMDReferenceStatus>(-1))
\r
409 template<typename Print>
\r
410 void detect_change(const Print& print)
\r
412 BMDReferenceStatus reference_status;
\r
414 if (output_->GetReferenceStatus(&reference_status) != S_OK)
\r
416 CASPAR_LOG(error) << print() << L" Reference signal: failed while querying status";
\r
418 else if (reference_status != last_reference_status_)
\r
420 last_reference_status_ = reference_status;
\r
422 if (reference_status == 0)
\r
423 CASPAR_LOG(info) << print() << L" Reference signal: not detected.";
\r
424 else if (reference_status & bmdReferenceNotSupportedByHardware)
\r
425 CASPAR_LOG(info) << print() << L" Reference signal: not supported by hardware.";
\r
426 else if (reference_status & bmdReferenceLocked)
\r
427 CASPAR_LOG(info) << print() << L" Reference signal: locked.";
\r
429 CASPAR_LOG(info) << print() << L" Reference signal: Unhandled enum bitfield: " << reference_status;
\r