Decklink helper
This commit is contained in:
@@ -81,21 +81,11 @@ OpenGLComposite::~OpenGLComposite()
|
||||
|
||||
bool OpenGLComposite::InitDeckLink()
|
||||
{
|
||||
bool bSuccess = false;
|
||||
IDeckLinkIterator* pDLIterator = NULL;
|
||||
IDeckLink* pDL = NULL;
|
||||
IDeckLinkProfileAttributes* deckLinkAttributes = NULL;
|
||||
IDeckLinkDisplayModeIterator* pDLInputDisplayModeIterator = NULL;
|
||||
IDeckLinkDisplayModeIterator* pDLOutputDisplayModeIterator = NULL;
|
||||
IDeckLinkDisplayMode* pDLInputDisplayMode = NULL;
|
||||
IDeckLinkDisplayMode* pDLOutputDisplayMode = NULL;
|
||||
BMDDisplayMode inputDisplayMode = bmdModeHD1080p5994;
|
||||
BMDDisplayMode outputDisplayMode = bmdModeHD1080p5994;
|
||||
std::string inputDisplayModeName = "1080p59.94";
|
||||
std::string outputDisplayModeName = "1080p59.94";
|
||||
std::string initFailureReason;
|
||||
int outputFrameRowBytes;
|
||||
HRESULT result;
|
||||
|
||||
if (mRuntimeHost && mRuntimeHost->GetRepoRoot().empty())
|
||||
{
|
||||
@@ -124,131 +114,16 @@ bool OpenGLComposite::InitDeckLink()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
mDeckLink->inputDisplayModeName = inputDisplayModeName;
|
||||
mDeckLink->outputDisplayModeName = outputDisplayModeName;
|
||||
|
||||
result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&pDLIterator);
|
||||
if (FAILED(result))
|
||||
if (!mDeckLink->DiscoverDevicesAndModes(inputDisplayMode, outputDisplayMode, inputDisplayModeName, outputDisplayModeName, initFailureReason))
|
||||
{
|
||||
MessageBox(NULL, _T("Please install the Blackmagic DeckLink drivers to use the features of this application."), _T("This application requires the DeckLink drivers installed."), MB_OK);
|
||||
const char* title = initFailureReason == "Please install the Blackmagic DeckLink drivers to use the features of this application."
|
||||
? "This application requires the DeckLink drivers installed."
|
||||
: "DeckLink initialization failed";
|
||||
MessageBoxA(NULL, initFailureReason.c_str(), title, MB_OK | MB_ICONERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
while (pDLIterator->Next(&pDL) == S_OK)
|
||||
{
|
||||
int64_t duplexMode;
|
||||
bool supportsInternalKeying = false;
|
||||
bool supportsExternalKeying = false;
|
||||
std::string modelName;
|
||||
|
||||
if (result = pDL->QueryInterface(IID_IDeckLinkProfileAttributes, (void**)&deckLinkAttributes) != S_OK)
|
||||
{
|
||||
printf("Could not obtain the IDeckLinkProfileAttributes interface - result %08x\n", result);
|
||||
pDL->Release();
|
||||
pDL = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
result = deckLinkAttributes->GetInt(BMDDeckLinkDuplex, &duplexMode);
|
||||
BOOL attributeFlag = FALSE;
|
||||
if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &attributeFlag) == S_OK)
|
||||
supportsInternalKeying = (attributeFlag != FALSE);
|
||||
attributeFlag = FALSE;
|
||||
if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &attributeFlag) == S_OK)
|
||||
supportsExternalKeying = (attributeFlag != FALSE);
|
||||
BSTR modelNameBstr = NULL;
|
||||
if (deckLinkAttributes->GetString(BMDDeckLinkModelName, &modelNameBstr) == S_OK && modelNameBstr != NULL)
|
||||
{
|
||||
const int requiredBytes = WideCharToMultiByte(CP_UTF8, 0, modelNameBstr, -1, NULL, 0, NULL, NULL);
|
||||
if (requiredBytes > 1)
|
||||
{
|
||||
std::vector<char> utf8Name(static_cast<std::size_t>(requiredBytes), '\0');
|
||||
if (WideCharToMultiByte(CP_UTF8, 0, modelNameBstr, -1, utf8Name.data(), requiredBytes, NULL, NULL) > 0)
|
||||
modelName.assign(utf8Name.data());
|
||||
}
|
||||
SysFreeString(modelNameBstr);
|
||||
}
|
||||
deckLinkAttributes->Release();
|
||||
deckLinkAttributes = NULL;
|
||||
|
||||
if (result != S_OK || duplexMode == bmdDuplexInactive)
|
||||
{
|
||||
pDL->Release();
|
||||
pDL = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Preserve the original input-then-output selection for half-duplex cards.
|
||||
// Input is optional later, but choosing output first can pick the wrong card.
|
||||
bool inputUsed = false;
|
||||
if (!mDeckLink->input && pDL->QueryInterface(IID_IDeckLinkInput, (void**)&mDeckLink->input) == S_OK)
|
||||
inputUsed = true;
|
||||
|
||||
if (!mDeckLink->output && (!inputUsed || (duplexMode == bmdDuplexFull)))
|
||||
{
|
||||
if (pDL->QueryInterface(IID_IDeckLinkOutput, (void**)&mDeckLink->output) != S_OK)
|
||||
mDeckLink->output = NULL;
|
||||
else
|
||||
{
|
||||
mDeckLink->outputModelName = modelName;
|
||||
mDeckLink->supportsInternalKeying = supportsInternalKeying;
|
||||
mDeckLink->supportsExternalKeying = supportsExternalKeying;
|
||||
}
|
||||
}
|
||||
|
||||
pDL->Release();
|
||||
pDL = NULL;
|
||||
|
||||
if (mDeckLink->output && mDeckLink->input)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mDeckLink->output)
|
||||
{
|
||||
MessageBox(NULL, _T("Expected an Output DeckLink device"), _T("This application requires a DeckLink output device."), MB_OK);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (mDeckLink->input && mDeckLink->input->GetDisplayModeIterator(&pDLInputDisplayModeIterator) != S_OK)
|
||||
{
|
||||
MessageBox(NULL, _T("Cannot get input Display Mode Iterator."), _T("DeckLink error."), MB_OK);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (mDeckLink->input && !FindDeckLinkDisplayMode(pDLInputDisplayModeIterator, inputDisplayMode, &pDLInputDisplayMode))
|
||||
{
|
||||
const std::string error = "Cannot get specified input BMDDisplayMode for configured mode: " + inputDisplayModeName;
|
||||
MessageBoxA(NULL, error.c_str(), "DeckLink input error.", MB_OK);
|
||||
goto error;
|
||||
}
|
||||
if (pDLInputDisplayModeIterator)
|
||||
{
|
||||
pDLInputDisplayModeIterator->Release();
|
||||
pDLInputDisplayModeIterator = NULL;
|
||||
}
|
||||
|
||||
if (mDeckLink->output->GetDisplayModeIterator(&pDLOutputDisplayModeIterator) != S_OK)
|
||||
{
|
||||
MessageBox(NULL, _T("Cannot get output Display Mode Iterator."), _T("DeckLink error."), MB_OK);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!FindDeckLinkDisplayMode(pDLOutputDisplayModeIterator, outputDisplayMode, &pDLOutputDisplayMode))
|
||||
{
|
||||
const std::string error = "Cannot get specified output BMDDisplayMode for configured mode: " + outputDisplayModeName;
|
||||
MessageBoxA(NULL, error.c_str(), "DeckLink output error.", MB_OK);
|
||||
goto error;
|
||||
}
|
||||
pDLOutputDisplayModeIterator->Release();
|
||||
pDLOutputDisplayModeIterator = NULL;
|
||||
|
||||
mDeckLink->outputFrameWidth = pDLOutputDisplayMode->GetWidth();
|
||||
mDeckLink->outputFrameHeight = pDLOutputDisplayMode->GetHeight();
|
||||
mDeckLink->inputFrameWidth = pDLInputDisplayMode ? pDLInputDisplayMode->GetWidth() : mDeckLink->outputFrameWidth;
|
||||
mDeckLink->inputFrameHeight = pDLInputDisplayMode ? pDLInputDisplayMode->GetHeight() : mDeckLink->outputFrameHeight;
|
||||
if (!mDeckLink->input)
|
||||
mDeckLink->inputDisplayModeName = "No input - black frame";
|
||||
|
||||
if (! CheckOpenGLExtensions())
|
||||
{
|
||||
initFailureReason = "OpenGL extension checks failed.";
|
||||
@@ -266,22 +141,9 @@ bool OpenGLComposite::InitDeckLink()
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (mRuntimeHost)
|
||||
{
|
||||
mDeckLink->statusMessage = mDeckLink->outputModelName.empty()
|
||||
? "DeckLink output device selected."
|
||||
: ("Selected output device: " + mDeckLink->outputModelName);
|
||||
mRuntimeHost->SetDeckLinkOutputStatus(
|
||||
mDeckLink->outputModelName,
|
||||
mDeckLink->supportsInternalKeying,
|
||||
mDeckLink->supportsExternalKeying,
|
||||
mDeckLink->keyerInterfaceAvailable,
|
||||
mRuntimeHost->ExternalKeyingEnabled(),
|
||||
mDeckLink->externalKeyingActive,
|
||||
mDeckLink->statusMessage);
|
||||
}
|
||||
|
||||
pDLOutputDisplayMode->GetFrameRate(&mDeckLink->frameDuration, &mDeckLink->frameTimescale);
|
||||
PublishDeckLinkOutputStatus(mDeckLink->outputModelName.empty()
|
||||
? "DeckLink output device selected."
|
||||
: ("Selected output device: " + mDeckLink->outputModelName));
|
||||
|
||||
// Resize window to match output video frame, but scale large formats down by half for viewing.
|
||||
if (mDeckLink->outputFrameWidth < 1920)
|
||||
@@ -299,180 +161,29 @@ bool OpenGLComposite::InitDeckLink()
|
||||
}
|
||||
}
|
||||
|
||||
if (mDeckLink->input)
|
||||
if (!mDeckLink->ConfigureInput(this, hGLDC, hGLRC, inputDisplayMode, initFailureReason))
|
||||
{
|
||||
// Use custom allocators so we pin only once then recycle them
|
||||
CComPtr<IDeckLinkVideoBufferAllocatorProvider> captureAllocator(new (std::nothrow) InputAllocatorPool(hGLDC, hGLRC));
|
||||
|
||||
if (mDeckLink->input->EnableVideoInputWithAllocatorProvider(inputDisplayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault, captureAllocator) != S_OK)
|
||||
{
|
||||
OutputDebugStringA("DeckLink input could not be enabled; continuing in output-only black-frame mode.\n");
|
||||
mDeckLink->input->Release();
|
||||
mDeckLink->input = NULL;
|
||||
mDeckLink->hasNoInputSource = true;
|
||||
mDeckLink->inputDisplayModeName = "No input - black frame";
|
||||
if (mRuntimeHost)
|
||||
mRuntimeHost->SetSignalStatus(false, mDeckLink->inputFrameWidth, mDeckLink->inputFrameHeight, mDeckLink->inputDisplayModeName);
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (mDeckLink->input)
|
||||
{
|
||||
mDeckLink->captureDelegate = new CaptureDelegate(this);
|
||||
if (mDeckLink->input->SetCallback(mDeckLink->captureDelegate) != S_OK)
|
||||
{
|
||||
initFailureReason = "DeckLink input setup failed while installing the capture callback.";
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else if (mRuntimeHost)
|
||||
if (!mDeckLink->input && mRuntimeHost)
|
||||
{
|
||||
mRuntimeHost->SetSignalStatus(false, mDeckLink->inputFrameWidth, mDeckLink->inputFrameHeight, mDeckLink->inputDisplayModeName);
|
||||
}
|
||||
|
||||
if (mDeckLink->output->RowBytesForPixelFormat(bmdFormat8BitBGRA, mDeckLink->outputFrameWidth, &outputFrameRowBytes) != S_OK)
|
||||
if (!mDeckLink->ConfigureOutput(this, hGLDC, hGLRC, outputDisplayMode, mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled(), initFailureReason))
|
||||
{
|
||||
initFailureReason = "DeckLink output setup failed while calculating BGRA row bytes.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Use a custom allocator so we pin only once then recycle them
|
||||
mDeckLink->playoutAllocator = new PinnedMemoryAllocator(hGLDC, hGLRC, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * mDeckLink->outputFrameHeight);
|
||||
PublishDeckLinkOutputStatus(mDeckLink->statusMessage);
|
||||
|
||||
if (mDeckLink->output->EnableVideoOutput(outputDisplayMode, bmdVideoOutputFlagDefault) != S_OK)
|
||||
{
|
||||
initFailureReason = "DeckLink output setup failed while enabling video output.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (mDeckLink->output->QueryInterface(IID_IDeckLinkKeyer, (void**)&mDeckLink->keyer) == S_OK && mDeckLink->keyer != NULL)
|
||||
mDeckLink->keyerInterfaceAvailable = true;
|
||||
|
||||
if (mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled())
|
||||
{
|
||||
if (!mDeckLink->supportsExternalKeying)
|
||||
{
|
||||
mDeckLink->statusMessage = "External keying was requested, but the selected DeckLink output does not report external keying support.";
|
||||
}
|
||||
else if (!mDeckLink->keyerInterfaceAvailable)
|
||||
{
|
||||
mDeckLink->statusMessage = "External keying was requested, but the selected DeckLink output does not expose the IDeckLinkKeyer interface.";
|
||||
}
|
||||
else if (mDeckLink->keyer->Enable(TRUE) != S_OK || mDeckLink->keyer->SetLevel(255) != S_OK)
|
||||
{
|
||||
mDeckLink->statusMessage = "External keying was requested, but enabling the DeckLink keyer failed.";
|
||||
}
|
||||
else
|
||||
{
|
||||
mDeckLink->externalKeyingActive = true;
|
||||
mDeckLink->statusMessage = "External keying is active on the selected DeckLink output.";
|
||||
}
|
||||
}
|
||||
else if (mDeckLink->supportsExternalKeying)
|
||||
{
|
||||
mDeckLink->statusMessage = "Selected DeckLink output supports external keying. Set enableExternalKeying to true in runtime-host.json to request it.";
|
||||
}
|
||||
|
||||
if (mRuntimeHost)
|
||||
{
|
||||
mRuntimeHost->SetDeckLinkOutputStatus(
|
||||
mDeckLink->outputModelName,
|
||||
mDeckLink->supportsInternalKeying,
|
||||
mDeckLink->supportsExternalKeying,
|
||||
mDeckLink->keyerInterfaceAvailable,
|
||||
mRuntimeHost->ExternalKeyingEnabled(),
|
||||
mDeckLink->externalKeyingActive,
|
||||
mDeckLink->statusMessage);
|
||||
}
|
||||
|
||||
// Create a queue of 10 IDeckLinkMutableVideoFrame objects to use for scheduling output video frames.
|
||||
// The ScheduledFrameCompleted() callback will immediately schedule a new frame using the next video frame from this queue.
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
// The frame read back from the GPU frame buffer and used for the playout video frame is in BGRA format.
|
||||
// The BGRA frame will be converted on playout to YCbCr either in hardware on most DeckLink cards or in software
|
||||
// within the DeckLink API for DeckLink devices without this hardware conversion.
|
||||
// If you want RGB 4:4:4 format to be played out "over the wire" in SDI, turn on the "Use 4:4:4 SDI" in the control
|
||||
// panel or turn on the bmdDeckLinkConfig444SDIVideoOutput flag using the IDeckLinkConfiguration interface.
|
||||
IDeckLinkMutableVideoFrame* outputFrame;
|
||||
IDeckLinkVideoBuffer* outputFrameBuffer = NULL;
|
||||
|
||||
if (mDeckLink->playoutAllocator->AllocateVideoBuffer(&outputFrameBuffer) != S_OK)
|
||||
{
|
||||
initFailureReason = "DeckLink output setup failed while allocating an output frame buffer.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (mDeckLink->output->CreateVideoFrameWithBuffer(mDeckLink->outputFrameWidth, mDeckLink->outputFrameHeight, outputFrameRowBytes, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, outputFrameBuffer, &outputFrame) != S_OK)
|
||||
{
|
||||
initFailureReason = "DeckLink output setup failed while creating an output video frame.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
mDeckLink->outputVideoFrameQueue.push_back(outputFrame);
|
||||
}
|
||||
|
||||
mDeckLink->playoutDelegate = new PlayoutDelegate(this);
|
||||
if (mDeckLink->playoutDelegate == NULL)
|
||||
{
|
||||
initFailureReason = "DeckLink output setup failed while creating the playout callback.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (mDeckLink->output->SetScheduledFrameCompletionCallback(mDeckLink->playoutDelegate) != S_OK)
|
||||
{
|
||||
initFailureReason = "DeckLink output setup failed while installing the scheduled-frame callback.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
bSuccess = true;
|
||||
return true;
|
||||
|
||||
error:
|
||||
if (!bSuccess)
|
||||
{
|
||||
if (!initFailureReason.empty())
|
||||
MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR);
|
||||
|
||||
mDeckLink->ReleaseResources();
|
||||
}
|
||||
|
||||
if (pDL != NULL)
|
||||
{
|
||||
pDL->Release();
|
||||
pDL = NULL;
|
||||
}
|
||||
|
||||
if (pDLInputDisplayMode != NULL)
|
||||
{
|
||||
pDLInputDisplayMode->Release();
|
||||
pDLInputDisplayMode = NULL;
|
||||
}
|
||||
|
||||
if (pDLOutputDisplayMode != NULL)
|
||||
{
|
||||
pDLOutputDisplayMode->Release();
|
||||
pDLOutputDisplayMode = NULL;
|
||||
}
|
||||
|
||||
if (pDLInputDisplayModeIterator != NULL)
|
||||
{
|
||||
pDLInputDisplayModeIterator->Release();
|
||||
pDLInputDisplayModeIterator = NULL;
|
||||
}
|
||||
|
||||
if (pDLOutputDisplayModeIterator != NULL)
|
||||
{
|
||||
pDLOutputDisplayModeIterator->Release();
|
||||
pDLOutputDisplayModeIterator = NULL;
|
||||
}
|
||||
|
||||
if (pDLIterator != NULL)
|
||||
{
|
||||
pDLIterator->Release();
|
||||
pDLIterator = NULL;
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
if (!initFailureReason.empty())
|
||||
MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR);
|
||||
mDeckLink->ReleaseResources();
|
||||
return false;
|
||||
}
|
||||
|
||||
void OpenGLComposite::paintGL()
|
||||
@@ -504,6 +215,24 @@ void OpenGLComposite::resizeWindow(int width, int height)
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLComposite::PublishDeckLinkOutputStatus(const std::string& statusMessage)
|
||||
{
|
||||
if (!mRuntimeHost)
|
||||
return;
|
||||
|
||||
if (!statusMessage.empty())
|
||||
mDeckLink->statusMessage = statusMessage;
|
||||
|
||||
mRuntimeHost->SetDeckLinkOutputStatus(
|
||||
mDeckLink->outputModelName,
|
||||
mDeckLink->supportsInternalKeying,
|
||||
mDeckLink->supportsExternalKeying,
|
||||
mDeckLink->keyerInterfaceAvailable,
|
||||
mRuntimeHost->ExternalKeyingEnabled(),
|
||||
mDeckLink->externalKeyingActive,
|
||||
mDeckLink->statusMessage);
|
||||
}
|
||||
|
||||
bool OpenGLComposite::InitOpenGLState()
|
||||
{
|
||||
if (! ResolveGLExtensions())
|
||||
@@ -717,17 +446,8 @@ bool OpenGLComposite::Stop()
|
||||
|
||||
const bool wasExternalKeyingActive = mDeckLink->externalKeyingActive;
|
||||
mDeckLink->Stop();
|
||||
if (wasExternalKeyingActive && mRuntimeHost)
|
||||
{
|
||||
mRuntimeHost->SetDeckLinkOutputStatus(
|
||||
mDeckLink->outputModelName,
|
||||
mDeckLink->supportsInternalKeying,
|
||||
mDeckLink->supportsExternalKeying,
|
||||
mDeckLink->keyerInterfaceAvailable,
|
||||
mRuntimeHost->ExternalKeyingEnabled(),
|
||||
mDeckLink->externalKeyingActive,
|
||||
"External keying has been disabled.");
|
||||
}
|
||||
if (wasExternalKeyingActive)
|
||||
PublishDeckLinkOutputStatus("External keying has been disabled.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ public:
|
||||
private:
|
||||
void resizeWindow(int width, int height);
|
||||
bool CheckOpenGLExtensions();
|
||||
void PublishDeckLinkOutputStatus(const std::string& statusMessage);
|
||||
using LayerProgram = OpenGLRenderer::LayerProgram;
|
||||
|
||||
HWND hGLWnd;
|
||||
|
||||
Reference in New Issue
Block a user