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 "DeckLinkDisplayMode.h"
#include "GlRenderConstants.h"
#include <atlbase.h>
#include <cstdio>
#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()
{
@@ -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)
{
totalPlayoutFrames = 0;

View File

@@ -15,6 +15,9 @@ public:
~DeckLinkSession();
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 Stop();