Input optional
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-05 22:52:41 +10:00
parent 536f65bf88
commit fecc936a14
2 changed files with 127 additions and 27 deletions

View File

@@ -252,6 +252,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
wglMakeCurrent( NULL, NULL );
if (pOpenGLComposite->Start())
break; // success
MessageBoxA(NULL, "The OpenGL/DeckLink runtime initialized, but playout failed to start. See the previous DeckLink start message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR);
}
else
{
MessageBoxA(NULL, "The OpenGL/DeckLink runtime failed to initialize. See the previous initialization message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR);
}
// Failed to initialize - cleanup

View File

@@ -466,6 +466,7 @@ bool OpenGLComposite::InitDeckLink()
BMDDisplayMode outputDisplayMode = bmdModeHD1080p5994;
std::string inputDisplayModeName = "1080p59.94";
std::string outputDisplayModeName = "1080p59.94";
std::string initFailureReason;
int outputFrameRowBytes;
HRESULT result;
@@ -550,8 +551,8 @@ bool OpenGLComposite::InitDeckLink()
continue;
}
// Use a full duplex device as capture and playback, or half-duplex device
// as capture or playback.
// 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 (!mDLInput && pDL->QueryInterface(IID_IDeckLinkInput, (void**)&mDLInput) == S_OK)
inputUsed = true;
@@ -575,26 +576,29 @@ bool OpenGLComposite::InitDeckLink()
break;
}
if (! mDLOutput || ! mDLInput)
if (!mDLOutput)
{
MessageBox(NULL, _T("Expected both Input and Output DeckLink devices"), _T("This application requires two DeckLink devices."), MB_OK);
MessageBox(NULL, _T("Expected an Output DeckLink device"), _T("This application requires a DeckLink output device."), MB_OK);
goto error;
}
if (mDLInput->GetDisplayModeIterator(&pDLInputDisplayModeIterator) != S_OK)
if (mDLInput && mDLInput->GetDisplayModeIterator(&pDLInputDisplayModeIterator) != S_OK)
{
MessageBox(NULL, _T("Cannot get input Display Mode Iterator."), _T("DeckLink error."), MB_OK);
goto error;
}
if (!FindDeckLinkDisplayMode(pDLInputDisplayModeIterator, inputDisplayMode, &pDLInputDisplayMode))
if (mDLInput && !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 (mDLOutput->GetDisplayModeIterator(&pDLOutputDisplayModeIterator) != S_OK)
{
@@ -611,13 +615,18 @@ bool OpenGLComposite::InitDeckLink()
pDLOutputDisplayModeIterator->Release();
pDLOutputDisplayModeIterator = NULL;
mInputFrameWidth = pDLInputDisplayMode->GetWidth();
mInputFrameHeight = pDLInputDisplayMode->GetHeight();
mOutputFrameWidth = pDLOutputDisplayMode->GetWidth();
mOutputFrameHeight = pDLOutputDisplayMode->GetHeight();
mInputFrameWidth = pDLInputDisplayMode ? pDLInputDisplayMode->GetWidth() : mOutputFrameWidth;
mInputFrameHeight = pDLInputDisplayMode ? pDLInputDisplayMode->GetHeight() : mOutputFrameHeight;
if (!mDLInput)
mInputDisplayModeName = "No input - black frame";
if (! CheckOpenGLExtensions())
{
initFailureReason = "OpenGL extension checks failed.";
goto error;
}
if (mInputFrameWidth != mOutputFrameWidth || mInputFrameHeight != mOutputFrameHeight)
{
mFastTransferExtensionAvailable = false;
@@ -625,7 +634,10 @@ bool OpenGLComposite::InitDeckLink()
}
if (! InitOpenGLState())
{
initFailureReason = "OpenGL state initialization failed.";
goto error;
}
if (mRuntimeHost)
{
@@ -660,26 +672,51 @@ bool OpenGLComposite::InitDeckLink()
}
}
if (mDLInput)
{
// Use custom allocators so we pin only once then recycle them
CComPtr<IDeckLinkVideoBufferAllocatorProvider> captureAllocator(new (std::nothrow) InputAllocatorPool(hGLDC, hGLRC));
if (mDLInput->EnableVideoInputWithAllocatorProvider(inputDisplayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault, captureAllocator) != S_OK)
goto error;
{
OutputDebugStringA("DeckLink input could not be enabled; continuing in output-only black-frame mode.\n");
mDLInput->Release();
mDLInput = NULL;
mHasNoInputSource = true;
mInputDisplayModeName = "No input - black frame";
if (mRuntimeHost)
mRuntimeHost->SetSignalStatus(false, mInputFrameWidth, mInputFrameHeight, mInputDisplayModeName);
}
}
if (mDLInput)
{
mCaptureDelegate = new CaptureDelegate(this);
if (mDLInput->SetCallback(mCaptureDelegate) != S_OK)
{
initFailureReason = "DeckLink input setup failed while installing the capture callback.";
goto error;
}
}
else if (mRuntimeHost)
{
mRuntimeHost->SetSignalStatus(false, mInputFrameWidth, mInputFrameHeight, mInputDisplayModeName);
}
if (mDLOutput->RowBytesForPixelFormat(bmdFormat8BitBGRA, mOutputFrameWidth, &outputFrameRowBytes) != S_OK)
{
initFailureReason = "DeckLink output setup failed while calculating BGRA row bytes.";
goto error;
}
// Use a custom allocator so we pin only once then recycle them
mPlayoutAllocator = new PinnedMemoryAllocator(hGLDC, hGLRC, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * mOutputFrameHeight);
if (mDLOutput->EnableVideoOutput(outputDisplayMode, bmdVideoOutputFlagDefault) != S_OK)
{
initFailureReason = "DeckLink output setup failed while enabling video output.";
goto error;
}
if (mDLOutput->QueryInterface(IID_IDeckLinkKeyer, (void**)&mDLKeyer) == S_OK && mDLKeyer != NULL)
mDeckLinkKeyerInterfaceAvailable = true;
@@ -734,26 +771,41 @@ bool OpenGLComposite::InitDeckLink()
IDeckLinkVideoBuffer* outputFrameBuffer = NULL;
if (mPlayoutAllocator->AllocateVideoBuffer(&outputFrameBuffer) != S_OK)
{
initFailureReason = "DeckLink output setup failed while allocating an output frame buffer.";
goto error;
}
if (mDLOutput->CreateVideoFrameWithBuffer(mOutputFrameWidth, mOutputFrameHeight, outputFrameRowBytes, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, outputFrameBuffer, &outputFrame) != S_OK)
{
initFailureReason = "DeckLink output setup failed while creating an output video frame.";
goto error;
}
mDLOutputVideoFrameQueue.push_back(outputFrame);
}
mPlayoutDelegate = new PlayoutDelegate(this);
if (mPlayoutDelegate == NULL)
{
initFailureReason = "DeckLink output setup failed while creating the playout callback.";
goto error;
}
if (mDLOutput->SetScheduledFrameCompletionCallback(mPlayoutDelegate) != S_OK)
{
initFailureReason = "DeckLink output setup failed while installing the scheduled-frame callback.";
goto error;
}
bSuccess = true;
error:
if (!bSuccess)
{
if (!initFailureReason.empty())
MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR);
if (mDLKeyer != NULL)
{
mDLKeyer->Disable();
@@ -1195,6 +1247,7 @@ void OpenGLComposite::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame,
if (mFastTransferExtensionAvailable)
{
// Finished with mCaptureTexture
if (!mHasNoInputSource)
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
if (! mPlayoutAllocator->transferFrame(pFrame, mOutputTexture))
@@ -1232,6 +1285,16 @@ void OpenGLComposite::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame,
bool OpenGLComposite::Start()
{
mTotalPlayoutFrames = 0;
if (!mDLOutput)
{
MessageBoxA(NULL, "Cannot start playout because no DeckLink output device is available.", "DeckLink start failed", MB_OK | MB_ICONERROR);
return false;
}
if (mDLOutputVideoFrameQueue.empty())
{
MessageBoxA(NULL, "Cannot start playout because the output frame queue is empty.", "DeckLink start failed", MB_OK | MB_ICONERROR);
return false;
}
// Preroll frames
for (unsigned i = 0; i < kPrerollFrameCount; i++)
@@ -1244,11 +1307,15 @@ bool OpenGLComposite::Start()
// Start with a black frame for playout
IDeckLinkVideoBuffer* outputVideoFrameBuffer;
if (outputVideoFrame->QueryInterface(IID_IDeckLinkVideoBuffer, (void**)&outputVideoFrameBuffer) != S_OK)
{
MessageBoxA(NULL, "Could not query the preroll output frame buffer.", "DeckLink start failed", MB_OK | MB_ICONERROR);
return false;
}
if (outputVideoFrameBuffer->StartAccess(bmdBufferAccessWrite) != S_OK)
{
outputVideoFrameBuffer->Release();
MessageBoxA(NULL, "Could not write to the preroll output frame buffer.", "DeckLink start failed", MB_OK | MB_ICONERROR);
return false;
}
@@ -1260,13 +1327,27 @@ bool OpenGLComposite::Start()
outputVideoFrameBuffer->Release();
if (mDLOutput->ScheduleVideoFrame(outputVideoFrame, (mTotalPlayoutFrames * mFrameDuration), mFrameDuration, mFrameTimescale) != S_OK)
{
MessageBoxA(NULL, "Could not schedule a preroll output frame.", "DeckLink start failed", MB_OK | MB_ICONERROR);
return false;
}
mTotalPlayoutFrames++;
}
mDLInput->StartStreams();
mDLOutput->StartScheduledPlayback(0, mFrameTimescale, 1.0);
if (mDLInput)
{
if (mDLInput->StartStreams() != S_OK)
{
MessageBoxA(NULL, "Could not start the DeckLink input stream.", "DeckLink start failed", MB_OK | MB_ICONERROR);
return false;
}
}
if (mDLOutput->StartScheduledPlayback(0, mFrameTimescale, 1.0) != S_OK)
{
MessageBoxA(NULL, "Could not start DeckLink scheduled playback.", "DeckLink start failed", MB_OK | MB_ICONERROR);
return false;
}
return true;
}
@@ -1296,11 +1377,17 @@ bool OpenGLComposite::Stop()
}
}
if (mDLInput)
{
mDLInput->StopStreams();
mDLInput->DisableVideoInput();
}
if (mDLOutput)
{
mDLOutput->StopScheduledPlayback(0, NULL, 0);
mDLOutput->DisableVideoOutput();
}
return true;
}
@@ -1920,10 +2007,8 @@ void OpenGLComposite::renderEffect()
{
PollRuntimeChanges();
if (mHasNoInputSource)
return;
if (mFastTransferExtensionAvailable)
const bool hasInputSource = !mHasNoInputSource;
if (hasInputSource && mFastTransferExtensionAvailable)
{
// Signal that we're about to draw using mCaptureTexture onto mFBOTexture.
VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::CPUtoGPU);
@@ -1931,7 +2016,17 @@ void OpenGLComposite::renderEffect()
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
if (hasInputSource)
{
renderDecodePass();
}
else
{
glBindFramebuffer(GL_FRAMEBUFFER, mDecodeFrameBuf);
glViewport(0, 0, mInputFrameWidth, mInputFrameHeight);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
const std::vector<RuntimeRenderState> layerStates = mRuntimeHost ? mRuntimeHost->GetLayerRenderStates(mInputFrameWidth, mInputFrameHeight) : std::vector<RuntimeRenderState>();
if (layerStates.empty() || mLayerPrograms.empty())
@@ -1963,7 +2058,7 @@ void OpenGLComposite::renderEffect()
pushFramebufferToHistoryRing(mDecodeFrameBuf, mSourceHistoryRing);
if (mFastTransferExtensionAvailable)
if (hasInputSource && mFastTransferExtensionAvailable)
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
}