Decklink helper
Some checks failed
CI / Native Windows Build And Tests (push) Has been cancelled
CI / React UI Build (push) Has been cancelled
CI / Windows Release Package (push) Has been cancelled

This commit is contained in:
2026-05-06 10:44:55 +10:00
parent 6918306336
commit 35f5a024fd
4 changed files with 329 additions and 318 deletions

View File

@@ -1,8 +1,32 @@
#include "DeckLinkSession.h" #include "DeckLinkSession.h"
#include "DeckLinkDisplayMode.h"
#include "GlRenderConstants.h" #include "GlRenderConstants.h"
#include <atlbase.h>
#include <cstdio>
#include <cstring> #include <cstring>
#include <new>
#include <vector>
namespace
{
std::string BstrToUtf8(BSTR value)
{
if (value == NULL)
return std::string();
const int requiredBytes = WideCharToMultiByte(CP_UTF8, 0, value, -1, NULL, 0, NULL, NULL);
if (requiredBytes <= 1)
return std::string();
std::vector<char> utf8Name(static_cast<std::size_t>(requiredBytes), '\0');
if (WideCharToMultiByte(CP_UTF8, 0, value, -1, utf8Name.data(), requiredBytes, NULL, NULL) <= 0)
return std::string();
return std::string(utf8Name.data());
}
}
DeckLinkSession::~DeckLinkSession() DeckLinkSession::~DeckLinkSession()
{ {
@@ -57,6 +81,269 @@ void DeckLinkSession::ReleaseResources()
} }
} }
bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, BMDDisplayMode outputDisplayMode, const std::string& requestedInputDisplayModeName, const std::string& requestedOutputDisplayModeName, std::string& error)
{
bool success = false;
IDeckLinkIterator* deckLinkIterator = NULL;
IDeckLink* deckLink = NULL;
IDeckLinkProfileAttributes* deckLinkAttributes = NULL;
IDeckLinkDisplayModeIterator* inputDisplayModeIterator = NULL;
IDeckLinkDisplayModeIterator* outputDisplayModeIterator = NULL;
IDeckLinkDisplayMode* inputMode = NULL;
IDeckLinkDisplayMode* outputMode = NULL;
inputDisplayModeName = requestedInputDisplayModeName;
outputDisplayModeName = requestedOutputDisplayModeName;
HRESULT result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&deckLinkIterator);
if (FAILED(result))
{
error = "Please install the Blackmagic DeckLink drivers to use the features of this application.";
return false;
}
while (deckLinkIterator->Next(&deckLink) == S_OK)
{
int64_t duplexMode;
bool deviceSupportsInternalKeying = false;
bool deviceSupportsExternalKeying = false;
std::string modelName;
if (deckLink->QueryInterface(IID_IDeckLinkProfileAttributes, (void**)&deckLinkAttributes) != S_OK)
{
printf("Could not obtain the IDeckLinkProfileAttributes interface\n");
deckLink->Release();
deckLink = NULL;
continue;
}
result = deckLinkAttributes->GetInt(BMDDeckLinkDuplex, &duplexMode);
BOOL attributeFlag = FALSE;
if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &attributeFlag) == S_OK)
deviceSupportsInternalKeying = (attributeFlag != FALSE);
attributeFlag = FALSE;
if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &attributeFlag) == S_OK)
deviceSupportsExternalKeying = (attributeFlag != FALSE);
BSTR modelNameBstr = NULL;
if (deckLinkAttributes->GetString(BMDDeckLinkModelName, &modelNameBstr) == S_OK)
{
modelName = BstrToUtf8(modelNameBstr);
if (modelNameBstr != NULL)
SysFreeString(modelNameBstr);
}
deckLinkAttributes->Release();
deckLinkAttributes = NULL;
if (result != S_OK || duplexMode == bmdDuplexInactive)
{
deckLink->Release();
deckLink = NULL;
continue;
}
bool inputUsed = false;
if (!input && deckLink->QueryInterface(IID_IDeckLinkInput, (void**)&input) == S_OK)
inputUsed = true;
if (!output && (!inputUsed || (duplexMode == bmdDuplexFull)))
{
if (deckLink->QueryInterface(IID_IDeckLinkOutput, (void**)&output) != S_OK)
output = NULL;
else
{
outputModelName = modelName;
supportsInternalKeying = deviceSupportsInternalKeying;
supportsExternalKeying = deviceSupportsExternalKeying;
}
}
deckLink->Release();
deckLink = NULL;
if (output && input)
break;
}
if (!output)
{
error = "Expected an Output DeckLink device";
goto cleanup;
}
if (input && input->GetDisplayModeIterator(&inputDisplayModeIterator) != S_OK)
{
error = "Cannot get input Display Mode Iterator.";
goto cleanup;
}
if (input && !FindDeckLinkDisplayMode(inputDisplayModeIterator, inputDisplayMode, &inputMode))
{
error = "Cannot get specified input BMDDisplayMode for configured mode: " + requestedInputDisplayModeName;
goto cleanup;
}
if (inputDisplayModeIterator)
{
inputDisplayModeIterator->Release();
inputDisplayModeIterator = NULL;
}
if (output->GetDisplayModeIterator(&outputDisplayModeIterator) != S_OK)
{
error = "Cannot get output Display Mode Iterator.";
goto cleanup;
}
if (!FindDeckLinkDisplayMode(outputDisplayModeIterator, outputDisplayMode, &outputMode))
{
error = "Cannot get specified output BMDDisplayMode for configured mode: " + requestedOutputDisplayModeName;
goto cleanup;
}
outputDisplayModeIterator->Release();
outputDisplayModeIterator = NULL;
outputFrameWidth = outputMode->GetWidth();
outputFrameHeight = outputMode->GetHeight();
inputFrameWidth = inputMode ? inputMode->GetWidth() : outputFrameWidth;
inputFrameHeight = inputMode ? inputMode->GetHeight() : outputFrameHeight;
if (!input)
inputDisplayModeName = "No input - black frame";
outputMode->GetFrameRate(&frameDuration, &frameTimescale);
success = true;
cleanup:
if (!success)
ReleaseResources();
if (deckLink != NULL)
deckLink->Release();
if (deckLinkAttributes != NULL)
deckLinkAttributes->Release();
if (inputMode != NULL)
inputMode->Release();
if (outputMode != NULL)
outputMode->Release();
if (inputDisplayModeIterator != NULL)
inputDisplayModeIterator->Release();
if (outputDisplayModeIterator != NULL)
outputDisplayModeIterator->Release();
if (deckLinkIterator != NULL)
deckLinkIterator->Release();
return success;
}
bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, BMDDisplayMode inputDisplayMode, std::string& error)
{
if (!input)
{
hasNoInputSource = true;
inputDisplayModeName = "No input - black frame";
return true;
}
CComPtr<IDeckLinkVideoBufferAllocatorProvider> captureAllocator(new (std::nothrow) InputAllocatorPool(hdc, hglrc));
if (input->EnableVideoInputWithAllocatorProvider(inputDisplayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault, captureAllocator) != S_OK)
{
OutputDebugStringA("DeckLink input could not be enabled; continuing in output-only black-frame mode.\n");
input->Release();
input = NULL;
hasNoInputSource = true;
inputDisplayModeName = "No input - black frame";
return true;
}
captureDelegate = new CaptureDelegate(owner);
if (input->SetCallback(captureDelegate) != S_OK)
{
error = "DeckLink input setup failed while installing the capture callback.";
return false;
}
return true;
}
bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, BMDDisplayMode outputDisplayMode, bool externalKeyingEnabled, std::string& error)
{
int outputFrameRowBytes = 0;
if (output->RowBytesForPixelFormat(bmdFormat8BitBGRA, outputFrameWidth, &outputFrameRowBytes) != S_OK)
{
error = "DeckLink output setup failed while calculating BGRA row bytes.";
return false;
}
playoutAllocator = new PinnedMemoryAllocator(hdc, hglrc, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * outputFrameHeight);
if (output->EnableVideoOutput(outputDisplayMode, bmdVideoOutputFlagDefault) != S_OK)
{
error = "DeckLink output setup failed while enabling video output.";
return false;
}
if (output->QueryInterface(IID_IDeckLinkKeyer, (void**)&keyer) == S_OK && keyer != NULL)
keyerInterfaceAvailable = true;
if (externalKeyingEnabled)
{
if (!supportsExternalKeying)
{
statusMessage = "External keying was requested, but the selected DeckLink output does not report external keying support.";
}
else if (!keyerInterfaceAvailable)
{
statusMessage = "External keying was requested, but the selected DeckLink output does not expose the IDeckLinkKeyer interface.";
}
else if (keyer->Enable(TRUE) != S_OK || keyer->SetLevel(255) != S_OK)
{
statusMessage = "External keying was requested, but enabling the DeckLink keyer failed.";
}
else
{
externalKeyingActive = true;
statusMessage = "External keying is active on the selected DeckLink output.";
}
}
else if (supportsExternalKeying)
{
statusMessage = "Selected DeckLink output supports external keying. Set enableExternalKeying to true in runtime-host.json to request it.";
}
for (int i = 0; i < 10; i++)
{
IDeckLinkMutableVideoFrame* outputFrame = NULL;
IDeckLinkVideoBuffer* outputFrameBuffer = NULL;
if (playoutAllocator->AllocateVideoBuffer(&outputFrameBuffer) != S_OK)
{
error = "DeckLink output setup failed while allocating an output frame buffer.";
return false;
}
if (output->CreateVideoFrameWithBuffer(outputFrameWidth, outputFrameHeight, outputFrameRowBytes, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, outputFrameBuffer, &outputFrame) != S_OK)
{
error = "DeckLink output setup failed while creating an output video frame.";
outputFrameBuffer->Release();
return false;
}
outputVideoFrameQueue.push_back(outputFrame);
}
playoutDelegate = new PlayoutDelegate(owner);
if (playoutDelegate == NULL)
{
error = "DeckLink output setup failed while creating the playout callback.";
return false;
}
if (output->SetScheduledFrameCompletionCallback(playoutDelegate) != S_OK)
{
error = "DeckLink output setup failed while installing the scheduled-frame callback.";
return false;
}
return true;
}
bool DeckLinkSession::Start(unsigned outputHeight) bool DeckLinkSession::Start(unsigned outputHeight)
{ {
totalPlayoutFrames = 0; totalPlayoutFrames = 0;

View File

@@ -15,6 +15,9 @@ public:
~DeckLinkSession(); ~DeckLinkSession();
void ReleaseResources(); void ReleaseResources();
bool DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, BMDDisplayMode outputDisplayMode, const std::string& requestedInputDisplayModeName, const std::string& requestedOutputDisplayModeName, std::string& error);
bool ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, BMDDisplayMode inputDisplayMode, std::string& error);
bool ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, BMDDisplayMode outputDisplayMode, bool externalKeyingEnabled, std::string& error);
bool Start(unsigned outputFrameHeight); bool Start(unsigned outputFrameHeight);
bool Stop(); bool Stop();

View File

@@ -81,21 +81,11 @@ OpenGLComposite::~OpenGLComposite()
bool OpenGLComposite::InitDeckLink() 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 inputDisplayMode = bmdModeHD1080p5994;
BMDDisplayMode outputDisplayMode = bmdModeHD1080p5994; BMDDisplayMode outputDisplayMode = bmdModeHD1080p5994;
std::string inputDisplayModeName = "1080p59.94"; std::string inputDisplayModeName = "1080p59.94";
std::string outputDisplayModeName = "1080p59.94"; std::string outputDisplayModeName = "1080p59.94";
std::string initFailureReason; std::string initFailureReason;
int outputFrameRowBytes;
HRESULT result;
if (mRuntimeHost && mRuntimeHost->GetRepoRoot().empty()) if (mRuntimeHost && mRuntimeHost->GetRepoRoot().empty())
{ {
@@ -124,131 +114,16 @@ bool OpenGLComposite::InitDeckLink()
return false; return false;
} }
} }
mDeckLink->inputDisplayModeName = inputDisplayModeName;
mDeckLink->outputDisplayModeName = outputDisplayModeName;
result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&pDLIterator); if (!mDeckLink->DiscoverDevicesAndModes(inputDisplayMode, outputDisplayMode, inputDisplayModeName, outputDisplayModeName, initFailureReason))
if (FAILED(result))
{ {
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; 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()) if (! CheckOpenGLExtensions())
{ {
initFailureReason = "OpenGL extension checks failed."; initFailureReason = "OpenGL extension checks failed.";
@@ -266,22 +141,9 @@ bool OpenGLComposite::InitDeckLink()
goto error; goto error;
} }
if (mRuntimeHost) PublishDeckLinkOutputStatus(mDeckLink->outputModelName.empty()
{ ? "DeckLink output device selected."
mDeckLink->statusMessage = mDeckLink->outputModelName.empty() : ("Selected output device: " + mDeckLink->outputModelName));
? "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);
// Resize window to match output video frame, but scale large formats down by half for viewing. // Resize window to match output video frame, but scale large formats down by half for viewing.
if (mDeckLink->outputFrameWidth < 1920) 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 goto error;
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);
}
} }
if (!mDeckLink->input && mRuntimeHost)
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)
{ {
mRuntimeHost->SetSignalStatus(false, mDeckLink->inputFrameWidth, mDeckLink->inputFrameHeight, mDeckLink->inputDisplayModeName); 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; goto error;
} }
// Use a custom allocator so we pin only once then recycle them PublishDeckLinkOutputStatus(mDeckLink->statusMessage);
mDeckLink->playoutAllocator = new PinnedMemoryAllocator(hGLDC, hGLRC, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * mDeckLink->outputFrameHeight);
if (mDeckLink->output->EnableVideoOutput(outputDisplayMode, bmdVideoOutputFlagDefault) != S_OK) return true;
{
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;
error: error:
if (!bSuccess) if (!initFailureReason.empty())
{ MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR);
if (!initFailureReason.empty()) mDeckLink->ReleaseResources();
MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR); return false;
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;
} }
void OpenGLComposite::paintGL() 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() bool OpenGLComposite::InitOpenGLState()
{ {
if (! ResolveGLExtensions()) if (! ResolveGLExtensions())
@@ -717,17 +446,8 @@ bool OpenGLComposite::Stop()
const bool wasExternalKeyingActive = mDeckLink->externalKeyingActive; const bool wasExternalKeyingActive = mDeckLink->externalKeyingActive;
mDeckLink->Stop(); mDeckLink->Stop();
if (wasExternalKeyingActive && mRuntimeHost) if (wasExternalKeyingActive)
{ PublishDeckLinkOutputStatus("External keying has been disabled.");
mRuntimeHost->SetDeckLinkOutputStatus(
mDeckLink->outputModelName,
mDeckLink->supportsInternalKeying,
mDeckLink->supportsExternalKeying,
mDeckLink->keyerInterfaceAvailable,
mRuntimeHost->ExternalKeyingEnabled(),
mDeckLink->externalKeyingActive,
"External keying has been disabled.");
}
return true; return true;
} }

View File

@@ -105,6 +105,7 @@ public:
private: private:
void resizeWindow(int width, int height); void resizeWindow(int width, int height);
bool CheckOpenGLExtensions(); bool CheckOpenGLExtensions();
void PublishDeckLinkOutputStatus(const std::string& statusMessage);
using LayerProgram = OpenGLRenderer::LayerProgram; using LayerProgram = OpenGLRenderer::LayerProgram;
HWND hGLWnd; HWND hGLWnd;