2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>
\r
4 * This file is part of CasparCG.
\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
21 #include "../../stdafx.h"
\r
23 #if defined(_MSC_VER)
\r
24 #pragma warning (push, 1) // TODO: Legacy code, just disable warnings
\r
25 #pragma warning (disable : 4244)
\r
28 #include "DecklinkVideoConsumer.h"
\r
29 #include "DeckLinkAPI_h.h"
\r
31 #include "../../frame/frame_format.h"
\r
32 #include "../../../common/utility/memory.h"
\r
34 #include "../../renderer/render_device.h"
\r
36 #include <tbb/concurrent_queue.h>
\r
37 #include <boost/thread.hpp>
\r
39 #pragma warning(push)
\r
40 #pragma warning(disable : 4996)
\r
42 #include <atlbase.h>
\r
45 #include <atlhost.h>
\r
47 #pragma warning(push)
\r
49 namespace caspar{ namespace decklink{
\r
51 struct DecklinkVideoConsumer::Implementation : public IDeckLinkVideoOutputCallback
\r
53 struct DecklinkFrameManager;
\r
54 struct DecklinkVideoFrame
\r
56 explicit DecklinkVideoFrame(DecklinkFrameManager* pFactory)
\r
58 IDeckLinkMutableVideoFrame* frame = NULL;
\r
59 const caspar::frame_format_desc& format_desc = pFactory->pConsumerImpl_->get_frame_format_desc();
\r
60 if(pFactory->pConsumerImpl_->pDecklinkOutput_->CreateVideoFrame(format_desc.width, format_desc.height, format_desc.size/format_desc.height, bmdFormat8BitBGRA, bmdFrameFlagDefault, &frame) != S_OK)
\r
62 throw std::exception("DECKLINK: Failed to create frame");
\r
64 pDecklinkFrame_ = frame;
\r
67 if(pDecklinkFrame_->GetBytes((void**)&pBytes_) != S_OK)
\r
68 throw std::exception("DECKLINK: Failed to get bytes to frame");
\r
71 unsigned char* data()
\r
76 size_t width() const { return 0; }
\r
77 size_t height() const { return 0; }
\r
84 unsigned int size() const
\r
86 return pDecklinkFrame_->GetRowBytes() * pDecklinkFrame_->GetHeight();
\r
89 IDeckLinkMutableVideoFrame* meta_data() const
\r
91 return pDecklinkFrame_;
\r
99 DecklinkFrameManager* pFactory_;
\r
100 CComPtr<IDeckLinkMutableVideoFrame> pDecklinkFrame_;
\r
101 unsigned char* pBytes_;
\r
104 struct DecklinkPlaybackStrategy
\r
106 explicit DecklinkPlaybackStrategy(Implementation* pConsumerImpl) : pConsumerImpl_(pConsumerImpl), currentReservedFrameIndex_(0), totalFramesScheduled_(0)
\r
108 for(int i = 0; i<4; ++i)
\r
110 reservedFrames_.push_back(pConsumerImpl_->pFrameManager_->CreateReservedFrame());
\r
114 std::shared_ptr<DecklinkVideoFrame> GetReservedFrame()
\r
116 std::shared_ptr<DecklinkVideoFrame> pResult;
\r
117 if(reservedFrames_.size() > currentReservedFrameIndex_)
\r
119 pResult = reservedFrames_[currentReservedFrameIndex_];
\r
120 currentReservedFrameIndex_ = (currentReservedFrameIndex_+1) & 3;
\r
125 void DisplayFrame(const gpu_frame_ptr& frame)
\r
129 std::shared_ptr<DecklinkVideoFrame> pTempFrame = GetReservedFrame();
\r
130 if(pTempFrame && frame->size() == pTempFrame->size())
\r
132 common::copy(pTempFrame->data(), frame->data(), pTempFrame->size());
\r
133 DoRender(pTempFrame);
\r
136 CASPAR_LOG(error) << "DECKLINK: Failed to get reserved frame";
\r
140 CASPAR_LOG(error) << "DECKLINK: Tried to render frame with no data";
\r
143 void DoRender(const std::shared_ptr<DecklinkVideoFrame>& frame)
\r
145 static DWORD lastTime = 0;
\r
146 static bool bDoLog = true;
\r
148 if(pConsumerImpl_->pDecklinkOutput_->DisplayVideoFrameSync(reinterpret_cast<IDeckLinkMutableVideoFrame*>(frame->meta_data())) != S_OK)
\r
152 CASPAR_LOG(error) << "DECKLINK: Failed to render frame";
\r
160 // lastFrameID_ = frame->ID();
\r
163 int totalFramesScheduled_;
\r
164 std::vector<std::shared_ptr<DecklinkVideoFrame> > reservedFrames_;
\r
165 unsigned int currentReservedFrameIndex_;
\r
166 Implementation* pConsumerImpl_;
\r
168 friend struct DecklinkPlaybackStrategy;
\r
170 struct DecklinkFrameManager
\r
172 explicit DecklinkFrameManager(Implementation* pConsumerImpl) : pConsumerImpl_(pConsumerImpl)
\r
176 std::shared_ptr<DecklinkVideoFrame> CreateReservedFrame() {
\r
177 return std::make_shared<DecklinkVideoFrame>(this);
\r
180 const caspar::frame_format_desc& get_frame_format_desc() const {
\r
181 return pConsumerImpl_->get_frame_format_desc();
\r
184 Implementation* pConsumerImpl_;
\r
187 typedef std::tr1::shared_ptr<DecklinkFrameManager> DecklinkFrameManagerPtr;
\r
190 CComPtr<IDeckLink> pDecklink_;
\r
191 CComQIPtr<IDeckLinkOutput> pDecklinkOutput_;
\r
192 CComQIPtr<IDeckLinkKeyer> pDecklinkKeyer_;
\r
194 std::shared_ptr<DecklinkPlaybackStrategy> pPlayback_;
\r
195 DecklinkFrameManagerPtr pFrameManager_;
\r
196 frame_format currentFormat_;
\r
197 frame_format_desc format_desc_;
\r
199 std::exception_ptr pException_;
\r
200 boost::thread thread_;
\r
201 tbb::concurrent_bounded_queue<gpu_frame_ptr> frameBuffer_;
\r
203 // IDeckLinkMutableVideoFrame* pNextFrame_;
\r
205 explicit Implementation(const caspar::frame_format_desc& format_desc, bool internalKey)
\r
206 : format_desc_(format_desc), currentFormat_(frame_format::pal), internalKey_(internalKey)
\r
209 CComPtr<IDeckLinkIterator> pDecklinkIterator;
\r
210 HRESULT result = pDecklinkIterator.CoCreateInstance(CLSID_CDeckLinkIterator);
\r
212 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("No Decklink drivers installed"));
\r
214 CComPtr<IDeckLink> pDecklink;
\r
215 IDeckLink* pTempDecklink = NULL;
\r
216 while(pDecklinkIterator->Next(&pTempDecklink) == S_OK)
\r
218 if(pDecklink == NULL)
\r
219 pDecklink = pTempDecklink;
\r
222 pTempDecklink->Release();
\r
223 pTempDecklink = NULL;
\r
226 if(pDecklink == nullptr)
\r
227 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("No Decklink card found"));
\r
230 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Failed to initialize decklink consumer."));
\r
232 pDecklink_ = pDecklink;
\r
234 frameBuffer_.set_capacity(1),
\r
235 thread_ = boost::thread([=]{Run();});
\r
240 frameBuffer_.push(nullptr);
\r
245 void DisplayFrame(const gpu_frame_ptr& frame)
\r
247 if(frame == nullptr)
\r
250 if(pException_ != nullptr)
\r
251 std::rethrow_exception(pException_);
\r
253 frameBuffer_.push(frame);
\r
262 gpu_frame_ptr frame;
\r
263 frameBuffer_.pop(frame);
\r
264 if(frame == nullptr)
\r
267 pPlayback_->DisplayFrame(frame);
\r
271 pException_ = std::current_exception();
\r
282 pDecklink_->GetModelName(&pModelName);
\r
283 if(pModelName != NULL)
\r
284 CASPAR_LOG(info) << "DECKLINK: Modelname: " << pModelName;
\r
286 pDecklinkOutput_ = pDecklink_;
\r
287 if(pDecklinkOutput_ == NULL) {
\r
288 CASPAR_LOG(info) << "DECKLINK: Failed to get IDecklinkOutput interface";
\r
292 unsigned long decklinkVideoFormat = GetDecklinkVideoFormat(format_desc_.format);
\r
293 if(decklinkVideoFormat == ULONG_MAX) {
\r
294 CASPAR_LOG(error) << "DECKLINK: Card does not support requested videoformat: " << format_desc_.name;
\r
298 currentFormat_ = format_desc_.format;
\r
300 BMDDisplayModeSupport displayModeSupport;
\r
301 if(FAILED(pDecklinkOutput_->DoesSupportVideoMode((BMDDisplayMode)decklinkVideoFormat, bmdFormat8BitBGRA, &displayModeSupport))) {
\r
302 CASPAR_LOG(error) << "DECKLINK: Card does not support requested videoformat";
\r
306 pDecklinkOutput_->DisableAudioOutput();
\r
307 if(FAILED(pDecklinkOutput_->EnableVideoOutput((BMDDisplayMode)decklinkVideoFormat, bmdVideoOutputFlagDefault))) {
\r
308 CASPAR_LOG(error) << "DECKLINK: Could not enable video output";
\r
312 pFrameManager_.reset(new DecklinkFrameManager(this));
\r
315 pDecklinkKeyer_ = pDecklink_;
\r
316 if(pDecklinkKeyer_) {
\r
317 bool bSuccess = true;
\r
318 if(FAILED(pDecklinkKeyer_->Enable(FALSE))) {
\r
319 CASPAR_LOG(error) << "DECKLINK: Failed to enable internal keyer";
\r
322 if(FAILED(pDecklinkKeyer_->SetLevel(255))) {
\r
323 CASPAR_LOG(error) << "DECKLINK: Keyer - Failed to set blend-level to max";
\r
328 CASPAR_LOG(info) << "DECKLINK: Successfully configured internal keyer";
\r
331 CASPAR_LOG(error) << "DECKLINK: Failed to get keyer-interface";
\r
335 pPlayback_ = std::make_shared<DecklinkPlaybackStrategy>(this);
\r
337 CASPAR_LOG(info) << "DECKLINK: Successfully initialized decklink for " << format_desc_.name;
\r
341 bool ReleaseDevice()
\r
343 pPlayback_.reset();
\r
345 if(pDecklinkKeyer_) {
\r
346 pDecklinkKeyer_.Release();
\r
349 if(pDecklinkOutput_) {
\r
350 BOOL bIsRunning = FALSE;
\r
351 pDecklinkOutput_->IsScheduledPlaybackRunning(&bIsRunning);
\r
353 pDecklinkOutput_->StopScheduledPlayback(0, NULL, 0);
\r
355 pDecklinkOutput_->DisableVideoOutput();
\r
361 //void DoScheduleNextFrame() {
\r
362 // static int frame = 0;
\r
363 // static bool bLog = true;
\r
364 // if(pDecklinkOutput_->ScheduleVideoFrame(pNextFrame_, frame++, 1, 25) != S_OK) {
\r
366 // LOG << TEXT("DECKLINK: Failed to display frame");
\r
371 // if(((frame-1) % 25) == 0)
\r
372 // LOG << TEXT("DECKLINK: Scheduled frame ") << (frame-1);
\r
377 // IUnknown needs o a dummy implementation
\r
378 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv)
\r
381 if(iid == IID_IUnknown) {
\r
385 if(iid == IID_IDeckLinkVideoOutputCallback_v7_1) {
\r
386 (*ppv) = (IDeckLinkVideoOutputCallback_v7_1*)this;
\r
389 if(iid == IID_IDeckLinkVideoOutputCallback) {
\r
390 (*ppv) = (IDeckLinkVideoOutputCallback*)this;
\r
394 return E_NOINTERFACE;
\r
396 virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
\r
397 virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
\r
399 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result) {
\r
400 //DoScheduleNextFrame();
\r
404 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
\r
409 const caspar::frame_format_desc& get_frame_format_desc() const
\r
411 return frame_format_desc::format_descs[currentFormat_];
\r
414 unsigned long GetDecklinkVideoFormat(frame_format fmt)
\r
418 case frame_format::pal: return bmdModePAL;
\r
419 case frame_format::ntsc: return bmdModeNTSC;
\r
420 case frame_format::x576p2500: return ULONG_MAX; //not supported
\r
421 case frame_format::x720p5000: return bmdModeHD720p50;
\r
422 case frame_format::x720p5994: return bmdModeHD720p5994;
\r
423 case frame_format::x720p6000: return bmdModeHD720p60;
\r
424 case frame_format::x1080p2397: return bmdModeHD1080p2398;
\r
425 case frame_format::x1080p2400: return bmdModeHD1080p24;
\r
426 case frame_format::x1080i5000: return bmdModeHD1080i50;
\r
427 case frame_format::x1080i5994: return bmdModeHD1080i5994;
\r
428 case frame_format::x1080i6000: return bmdModeHD1080i6000;
\r
429 case frame_format::x1080p2500: return bmdModeHD1080p25;
\r
430 case frame_format::x1080p2997: return bmdModeHD1080p2997;
\r
431 case frame_format::x1080p3000: return bmdModeHD1080p30;
\r
432 default: return ULONG_MAX;
\r
437 DecklinkVideoConsumer::DecklinkVideoConsumer(const caspar::frame_format_desc& format_desc, bool internalKey) : pImpl_(new Implementation(format_desc, internalKey))
\r
440 void DecklinkVideoConsumer::display(const gpu_frame_ptr& frame)
\r
442 pImpl_->DisplayFrame(frame);
\r
445 const frame_format_desc& DecklinkVideoConsumer::get_frame_format_desc() const
\r
447 return pImpl_->format_desc_;
\r
450 } //namespace decklink
\r