From 7f0f60c0e38a7b7661a936e9b36d10b50034c4e9 Mon Sep 17 00:00:00 2001
From: Aiden <68633820+awils27@users.noreply.github.com>
Date: Sun, 10 May 2026 23:31:45 +1000
Subject: [PATCH] ore untangling
---
CMakeLists.txt | 4 +
.../LoopThroughWithOpenGLCompositing.vcxproj | 4 +
...roughWithOpenGLCompositing.vcxproj.filters | 12 ++
.../gl/OpenGLComposite.cpp | 166 +++++++---------
.../gl/OpenGLComposite.h | 17 +-
.../gl/RenderEngine.cpp | 180 ++++++++++++++++++
.../gl/RenderEngine.h | 72 +++++++
.../gl/pipeline/OpenGLVideoIOBridge.cpp | 63 +-----
.../gl/pipeline/OpenGLVideoIOBridge.h | 21 +-
.../videoio/VideoBackend.cpp | 175 +++++++++++++++++
.../videoio/VideoBackend.h | 55 ++++++
11 files changed, 593 insertions(+), 176 deletions(-)
create mode 100644 apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.cpp
create mode 100644 apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.h
create mode 100644 apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.cpp
create mode 100644 apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3eb89ce..9a50320 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -62,6 +62,8 @@ set(APP_SOURCES
"${APP_DIR}/gl/OpenGLComposite.cpp"
"${APP_DIR}/gl/OpenGLComposite.h"
"${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp"
+ "${APP_DIR}/gl/RenderEngine.cpp"
+ "${APP_DIR}/gl/RenderEngine.h"
"${APP_DIR}/gl/pipeline/OpenGLRenderPass.cpp"
"${APP_DIR}/gl/pipeline/OpenGLRenderPass.h"
"${APP_DIR}/gl/pipeline/OpenGLRenderPipeline.cpp"
@@ -122,6 +124,8 @@ set(APP_SOURCES
"${APP_DIR}/targetver.h"
"${APP_DIR}/videoio/VideoIOFormat.cpp"
"${APP_DIR}/videoio/VideoIOFormat.h"
+ "${APP_DIR}/videoio/VideoBackend.cpp"
+ "${APP_DIR}/videoio/VideoBackend.h"
"${APP_DIR}/videoio/VideoIOTypes.h"
"${APP_DIR}/videoio/VideoPlayoutScheduler.cpp"
"${APP_DIR}/videoio/VideoPlayoutScheduler.h"
diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj
index e3e3669..e4e4868 100644
--- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj
+++ b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj
@@ -186,6 +186,7 @@
+
@@ -220,6 +221,7 @@
+
@@ -236,6 +238,7 @@
+
@@ -272,6 +275,7 @@
+
diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters
index cf8f9c8..b745b94 100644
--- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters
+++ b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters
@@ -48,6 +48,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -138,6 +141,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -179,6 +185,9 @@
Header Files
+
+ Header Files
+
Header Files
@@ -287,6 +296,9 @@
Header Files
+
+ Header Files
+
Header Files
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp
index 4e207bd..d24f81e 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp
@@ -1,16 +1,13 @@
#include "DeckLinkDisplayMode.h"
-#include "DeckLinkSession.h"
#include "OpenGLComposite.h"
#include "GLExtensions.h"
#include "GlRenderConstants.h"
-#include "OpenGLRenderPass.h"
-#include "OpenGLRenderPipeline.h"
-#include "OpenGLShaderPrograms.h"
-#include "OpenGLVideoIOBridge.h"
#include "PngScreenshotWriter.h"
+#include "RenderEngine.h"
#include "RuntimeParameterUtils.h"
#include "RuntimeServices.h"
#include "ShaderBuildQueue.h"
+#include "VideoBackend.h"
#include
#include
@@ -97,8 +94,6 @@ JsonValue BuildOscCommitValue(const ShaderParameterDefinition& definition, const
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC),
- mVideoIO(std::make_unique()),
- mRenderer(std::make_unique()),
mUseCommittedLayerStates(false),
mScreenshotRequested(false)
{
@@ -106,22 +101,16 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
mRuntimeHost = std::make_unique();
mRuntimeStore = std::make_unique(*mRuntimeHost);
mRuntimeSnapshotProvider = std::make_unique(*mRuntimeHost);
- mRenderPipeline = std::make_unique(
- *mRenderer,
+ mRenderEngine = std::make_unique(
*mRuntimeHost,
+ *mRuntimeSnapshotProvider,
+ pMutex,
+ hGLDC,
+ hGLRC,
[this]() { renderEffect(); },
[this]() { ProcessScreenshotRequest(); },
[this]() { paintGL(false); });
- mVideoIOBridge = std::make_unique(
- *mVideoIO,
- *mRenderer,
- *mRenderPipeline,
- *mRuntimeHost,
- pMutex,
- hGLDC,
- hGLRC);
- mRenderPass = std::make_unique(*mRenderer);
- mShaderPrograms = std::make_unique(*mRenderer, *mRuntimeHost, *mRuntimeSnapshotProvider);
+ mVideoBackend = std::make_unique(*mRenderEngine, mRuntimeHost->GetHealthTelemetry());
mShaderBuildQueue = std::make_unique(*mRuntimeSnapshotProvider);
mRuntimeServices = std::make_unique();
}
@@ -132,8 +121,8 @@ OpenGLComposite::~OpenGLComposite()
mRuntimeServices->Stop();
if (mShaderBuildQueue)
mShaderBuildQueue->Stop();
- mVideoIO->ReleaseResources();
- mRenderer->DestroyResources();
+ if (mVideoBackend)
+ mVideoBackend->ReleaseResources();
DeleteCriticalSection(&pMutex);
}
@@ -173,7 +162,7 @@ bool OpenGLComposite::InitVideoIO()
}
}
- if (!mVideoIO->DiscoverDevicesAndModes(videoModes, initFailureReason))
+ if (!mVideoBackend->DiscoverDevicesAndModes(videoModes, initFailureReason))
{
const char* title = initFailureReason == "Please install the Blackmagic DeckLink drivers to use the features of this application."
? "This application requires the DeckLink drivers installed."
@@ -182,7 +171,7 @@ bool OpenGLComposite::InitVideoIO()
return false;
}
const bool outputAlphaRequired = mRuntimeStore && mRuntimeStore->IsExternalKeyingConfigured();
- if (!mVideoIO->SelectPreferredFormats(videoModes, outputAlphaRequired, initFailureReason))
+ if (!mVideoBackend->SelectPreferredFormats(videoModes, outputAlphaRequired, initFailureReason))
goto error;
if (! CheckOpenGLExtensions())
@@ -197,38 +186,38 @@ bool OpenGLComposite::InitVideoIO()
goto error;
}
- PublishVideoIOStatus(mVideoIO->OutputModelName().empty()
+ PublishVideoIOStatus(mVideoBackend->OutputModelName().empty()
? "DeckLink output device selected."
- : ("Selected output device: " + mVideoIO->OutputModelName()));
+ : ("Selected output device: " + mVideoBackend->OutputModelName()));
// Resize window to match output video frame, but scale large formats down by half for viewing.
- if (mVideoIO->OutputFrameWidth() < 1920)
- resizeWindow(mVideoIO->OutputFrameWidth(), mVideoIO->OutputFrameHeight());
+ if (mVideoBackend->OutputFrameWidth() < 1920)
+ resizeWindow(mVideoBackend->OutputFrameWidth(), mVideoBackend->OutputFrameHeight());
else
- resizeWindow(mVideoIO->OutputFrameWidth() / 2, mVideoIO->OutputFrameHeight() / 2);
+ resizeWindow(mVideoBackend->OutputFrameWidth() / 2, mVideoBackend->OutputFrameHeight() / 2);
- if (!mVideoIO->ConfigureInput([this](const VideoIOFrame& frame) { mVideoIOBridge->VideoFrameArrived(frame); }, videoModes.input, initFailureReason))
+ if (!mVideoBackend->ConfigureInput(videoModes.input, initFailureReason))
{
goto error;
}
- if (!mVideoIO->HasInputDevice() && mRuntimeHost)
+ if (!mVideoBackend->HasInputDevice() && mRuntimeHost)
{
- mRuntimeHost->SetSignalStatus(false, mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), mVideoIO->InputDisplayModeName());
+ mRuntimeHost->SetSignalStatus(false, mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight(), mVideoBackend->InputDisplayModeName());
}
- if (!mVideoIO->ConfigureOutput([this](const VideoIOCompletion& completion) { mVideoIOBridge->PlayoutFrameCompleted(completion); }, videoModes.output, mRuntimeStore && mRuntimeStore->IsExternalKeyingConfigured(), initFailureReason))
+ if (!mVideoBackend->ConfigureOutput(videoModes.output, mRuntimeStore && mRuntimeStore->IsExternalKeyingConfigured(), initFailureReason))
{
goto error;
}
- PublishVideoIOStatus(mVideoIO->StatusMessage());
+ PublishVideoIOStatus(mVideoBackend->StatusMessage());
return true;
error:
if (!initFailureReason.empty())
MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR);
- mVideoIO->ReleaseResources();
+ mVideoBackend->ReleaseResources();
return false;
}
@@ -252,23 +241,21 @@ void OpenGLComposite::paintGL(bool force)
}
}
- if (!TryEnterCriticalSection(&pMutex))
+ if (!mRenderEngine->TryPresentToWindow(mVideoBackend->OutputFrameWidth(), mVideoBackend->OutputFrameHeight()))
{
ValidateRect(hGLWnd, NULL);
return;
}
- mRenderer->PresentToWindow(hGLDC, mVideoIO->OutputFrameWidth(), mVideoIO->OutputFrameHeight());
mLastPreviewPresentTime = std::chrono::steady_clock::now();
ValidateRect(hGLWnd, NULL);
- LeaveCriticalSection(&pMutex);
}
void OpenGLComposite::resizeGL(WORD width, WORD height)
{
// We don't set the project or model matrices here since the window data is copied directly from
// an off-screen FBO in paintGL(). Just save the width and height for use in paintGL().
- mRenderer->ResizeView(width, height);
+ mRenderEngine->ResizeView(width, height);
}
void OpenGLComposite::resizeWindow(int width, int height)
@@ -286,17 +273,17 @@ void OpenGLComposite::PublishVideoIOStatus(const std::string& statusMessage)
return;
if (!statusMessage.empty())
- mVideoIO->SetStatusMessage(statusMessage);
+ mVideoBackend->SetStatusMessage(statusMessage);
mRuntimeHost->SetVideoIOStatus(
"decklink",
- mVideoIO->OutputModelName(),
- mVideoIO->SupportsInternalKeying(),
- mVideoIO->SupportsExternalKeying(),
- mVideoIO->KeyerInterfaceAvailable(),
+ mVideoBackend->OutputModelName(),
+ mVideoBackend->SupportsInternalKeying(),
+ mVideoBackend->SupportsExternalKeying(),
+ mVideoBackend->KeyerInterfaceAvailable(),
mRuntimeStore ? mRuntimeStore->IsExternalKeyingConfigured() : false,
- mVideoIO->ExternalKeyingActive(),
- mVideoIO->StatusMessage());
+ mVideoBackend->ExternalKeyingActive(),
+ mVideoBackend->StatusMessage());
}
bool OpenGLComposite::InitOpenGLState()
@@ -319,41 +306,41 @@ bool OpenGLComposite::InitOpenGLState()
// Prepare the runtime shader program generated from the active shader package.
char compilerErrorMessage[1024];
- if (!mShaderPrograms->CompileDecodeShader(sizeof(compilerErrorMessage), compilerErrorMessage))
+ if (!mRenderEngine->CompileDecodeShader(sizeof(compilerErrorMessage), compilerErrorMessage))
{
MessageBoxA(NULL, compilerErrorMessage, "OpenGL decode shader failed to load or compile", MB_OK);
return false;
}
- if (!mShaderPrograms->CompileOutputPackShader(sizeof(compilerErrorMessage), compilerErrorMessage))
+ if (!mRenderEngine->CompileOutputPackShader(sizeof(compilerErrorMessage), compilerErrorMessage))
{
MessageBoxA(NULL, compilerErrorMessage, "OpenGL output pack shader failed to load or compile", MB_OK);
return false;
}
std::string rendererError;
- if (!mRenderer->InitializeResources(
- mVideoIO->InputFrameWidth(),
- mVideoIO->InputFrameHeight(),
- mVideoIO->CaptureTextureWidth(),
- mVideoIO->OutputFrameWidth(),
- mVideoIO->OutputFrameHeight(),
- mVideoIO->OutputPackTextureWidth(),
+ if (!mRenderEngine->InitializeResources(
+ mVideoBackend->InputFrameWidth(),
+ mVideoBackend->InputFrameHeight(),
+ mVideoBackend->CaptureTextureWidth(),
+ mVideoBackend->OutputFrameWidth(),
+ mVideoBackend->OutputFrameHeight(),
+ mVideoBackend->OutputPackTextureWidth(),
rendererError))
{
MessageBoxA(NULL, rendererError.c_str(), "OpenGL initialization error.", MB_OK);
return false;
}
- if (!mShaderPrograms->CompileLayerPrograms(mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage))
+ if (!mRenderEngine->CompileLayerPrograms(mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage))
{
MessageBoxA(NULL, compilerErrorMessage, "OpenGL shader failed to load or compile", MB_OK);
return false;
}
- mCachedLayerRenderStates = mShaderPrograms->CommittedLayerStates();
+ mCachedLayerRenderStates = mRenderEngine->CommittedLayerStates();
mUseCommittedLayerStates = false;
- mShaderPrograms->ResetTemporalHistoryState();
- mShaderPrograms->ResetShaderFeedbackState();
+ mRenderEngine->ResetTemporalHistoryState();
+ mRenderEngine->ResetShaderFeedbackState();
broadcastRuntimeState();
mRuntimeServices->BeginPolling(*mRuntimeHost);
@@ -362,7 +349,7 @@ bool OpenGLComposite::InitOpenGLState()
bool OpenGLComposite::Start()
{
- return mVideoIO->Start();
+ return mVideoBackend->Start();
}
bool OpenGLComposite::Stop()
@@ -370,8 +357,8 @@ bool OpenGLComposite::Stop()
if (mRuntimeServices)
mRuntimeServices->Stop();
- const bool wasExternalKeyingActive = mVideoIO->ExternalKeyingActive();
- mVideoIO->Stop();
+ const bool wasExternalKeyingActive = mVideoBackend->ExternalKeyingActive();
+ mVideoBackend->Stop();
if (wasExternalKeyingActive)
PublishVideoIOStatus("External keying has been disabled.");
@@ -587,19 +574,19 @@ void OpenGLComposite::renderEffect()
mOscOverlayStates.erase(overlayKey);
};
- const bool hasInputSource = mVideoIO->HasInputSource();
+ const bool hasInputSource = mVideoBackend->HasInputSource();
std::vector layerStates;
if (mUseCommittedLayerStates)
{
- layerStates = mShaderPrograms->CommittedLayerStates();
+ layerStates = mRenderEngine->CommittedLayerStates();
applyOscOverlays(layerStates, false);
if (mRuntimeSnapshotProvider)
mRuntimeSnapshotProvider->RefreshDynamicRenderStateFields(layerStates);
}
else if (mRuntimeSnapshotProvider)
{
- const unsigned renderWidth = mVideoIO->InputFrameWidth();
- const unsigned renderHeight = mVideoIO->InputFrameHeight();
+ const unsigned renderWidth = mVideoBackend->InputFrameWidth();
+ const unsigned renderHeight = mVideoBackend->InputFrameHeight();
const RuntimeSnapshotVersions versions = mRuntimeSnapshotProvider->GetVersions();
const bool renderStateCacheValid =
!mCachedLayerRenderStates.empty() &&
@@ -650,20 +637,14 @@ void OpenGLComposite::renderEffect()
}
}
const unsigned historyCap = mRuntimeStore ? mRuntimeStore->GetConfiguredMaxTemporalHistoryFrames() : 0;
- mRenderPass->Render(
+ mRenderEngine->RenderLayerStack(
hasInputSource,
layerStates,
- mVideoIO->InputFrameWidth(),
- mVideoIO->InputFrameHeight(),
- mVideoIO->CaptureTextureWidth(),
- mVideoIO->InputPixelFormat(),
- historyCap,
- [this](const RuntimeRenderState& state, LayerProgram::TextBinding& textBinding, std::string& error) {
- return mShaderPrograms->UpdateTextBindingTexture(state, textBinding, error);
- },
- [this](const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength, bool feedbackAvailable) {
- return mShaderPrograms->UpdateGlobalParamsBuffer(state, availableSourceHistoryLength, availableTemporalHistoryLength, feedbackAvailable);
- });
+ mVideoBackend->InputFrameWidth(),
+ mVideoBackend->InputFrameHeight(),
+ mVideoBackend->CaptureTextureWidth(),
+ mVideoBackend->InputPixelFormat(),
+ historyCap);
}
void OpenGLComposite::ProcessScreenshotRequest()
@@ -671,21 +652,16 @@ void OpenGLComposite::ProcessScreenshotRequest()
if (!mScreenshotRequested.exchange(false))
return;
- const unsigned width = mVideoIO ? mVideoIO->OutputFrameWidth() : 0;
- const unsigned height = mVideoIO ? mVideoIO->OutputFrameHeight() : 0;
+ const unsigned width = mVideoBackend ? mVideoBackend->OutputFrameWidth() : 0;
+ const unsigned height = mVideoBackend ? mVideoBackend->OutputFrameHeight() : 0;
if (width == 0 || height == 0)
return;
- std::vector bottomUpPixels(static_cast(width) * height * 4);
+ std::vector bottomUpPixels;
+ if (!mRenderEngine->ReadOutputFrameRgba(width, height, bottomUpPixels))
+ return;
std::vector topDownPixels(bottomUpPixels.size());
- glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer->OutputFramebuffer());
- glReadBuffer(GL_COLOR_ATTACHMENT0);
- glPixelStorei(GL_PACK_ALIGNMENT, 1);
- glPixelStorei(GL_PACK_ROW_LENGTH, 0);
- glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bottomUpPixels.data());
- glPixelStorei(GL_PACK_ALIGNMENT, 4);
-
const std::size_t rowBytes = static_cast(width) * 4;
for (unsigned y = 0; y < height; ++y)
{
@@ -752,7 +728,7 @@ bool OpenGLComposite::ProcessRuntimePollResults()
return true;
char compilerErrorMessage[1024] = {};
- if (!mShaderPrograms->CommitPreparedLayerPrograms(readyBuild, mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage))
+ if (!mRenderEngine->CommitPreparedLayerPrograms(readyBuild, mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight(), sizeof(compilerErrorMessage), compilerErrorMessage))
{
mRuntimeStore->SetCompileStatus(false, compilerErrorMessage);
mUseCommittedLayerStates = true;
@@ -762,10 +738,10 @@ bool OpenGLComposite::ProcessRuntimePollResults()
}
mUseCommittedLayerStates = false;
- mCachedLayerRenderStates = mShaderPrograms->CommittedLayerStates();
- mShaderPrograms->ResetTemporalHistoryState();
+ mCachedLayerRenderStates = mRenderEngine->CommittedLayerStates();
+ mRenderEngine->ResetTemporalHistoryState();
if (!mPreserveFeedbackOnNextShaderBuild)
- mShaderPrograms->ResetShaderFeedbackState();
+ mRenderEngine->ResetShaderFeedbackState();
mPreserveFeedbackOnNextShaderBuild = false;
broadcastRuntimeState();
return true;
@@ -780,13 +756,13 @@ bool OpenGLComposite::ProcessRuntimePollResults()
void OpenGLComposite::RequestShaderBuild()
{
- if (!mShaderBuildQueue || !mVideoIO)
+ if (!mShaderBuildQueue || !mVideoBackend)
return;
mUseCommittedLayerStates = true;
if (mRuntimeHost)
mRuntimeStore->ClearReloadRequest();
- mShaderBuildQueue->RequestBuild(mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight());
+ mShaderBuildQueue->RequestBuild(mVideoBackend->InputFrameWidth(), mVideoBackend->InputFrameHeight());
}
void OpenGLComposite::broadcastRuntimeState()
@@ -797,8 +773,8 @@ void OpenGLComposite::broadcastRuntimeState()
void OpenGLComposite::resetTemporalHistoryState()
{
- mShaderPrograms->ResetTemporalHistoryState();
- mShaderPrograms->ResetShaderFeedbackState();
+ mRenderEngine->ResetTemporalHistoryState();
+ mRenderEngine->ResetShaderFeedbackState();
}
bool OpenGLComposite::CheckOpenGLExtensions()
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h
index 1e41bab..a50c5a8 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h
@@ -12,7 +12,6 @@
#include
#include "GLExtensions.h"
-#include "OpenGLRenderer.h"
#include "RuntimeHost.h"
#include "RuntimeSnapshotProvider.h"
#include "RuntimeStore.h"
@@ -27,13 +26,10 @@
#include
#include
-class VideoIODevice;
-class OpenGLVideoIOBridge;
-class OpenGLRenderPass;
-class OpenGLRenderPipeline;
-class OpenGLShaderPrograms;
+class RenderEngine;
class RuntimeServices;
class ShaderBuildQueue;
+class VideoBackend;
class OpenGLComposite
@@ -74,7 +70,6 @@ private:
void resizeWindow(int width, int height);
bool CheckOpenGLExtensions();
void PublishVideoIOStatus(const std::string& statusMessage);
- using LayerProgram = OpenGLRenderer::LayerProgram;
struct OscOverlayState
{
std::string layerKey;
@@ -94,17 +89,13 @@ private:
HGLRC hGLRC;
CRITICAL_SECTION pMutex;
- std::unique_ptr mVideoIO;
- std::unique_ptr mRenderer;
std::unique_ptr mRuntimeHost;
std::unique_ptr mRuntimeStore;
std::unique_ptr mRuntimeSnapshotProvider;
- std::unique_ptr mVideoIOBridge;
- std::unique_ptr mRenderPass;
- std::unique_ptr mRenderPipeline;
- std::unique_ptr mShaderPrograms;
+ std::unique_ptr mRenderEngine;
std::unique_ptr mShaderBuildQueue;
std::unique_ptr mRuntimeServices;
+ std::unique_ptr mVideoBackend;
std::vector mCachedLayerRenderStates;
uint64_t mCachedRenderStateVersion = 0;
uint64_t mCachedParameterStateVersion = 0;
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.cpp
new file mode 100644
index 0000000..0e836d4
--- /dev/null
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.cpp
@@ -0,0 +1,180 @@
+#include "RenderEngine.h"
+
+#include
+
+RenderEngine::RenderEngine(
+ RuntimeHost& runtimeHost,
+ RuntimeSnapshotProvider& runtimeSnapshotProvider,
+ CRITICAL_SECTION& mutex,
+ HDC hdc,
+ HGLRC hglrc,
+ RenderEffectCallback renderEffect,
+ ScreenshotCallback screenshotReady,
+ PreviewPaintCallback previewPaint) :
+ mRenderer(),
+ mRenderPass(mRenderer),
+ mRenderPipeline(mRenderer, runtimeHost, std::move(renderEffect), std::move(screenshotReady), std::move(previewPaint)),
+ mShaderPrograms(mRenderer, runtimeHost, runtimeSnapshotProvider),
+ mMutex(mutex),
+ mHdc(hdc),
+ mHglrc(hglrc)
+{
+}
+
+RenderEngine::~RenderEngine()
+{
+ mRenderer.DestroyResources();
+}
+
+bool RenderEngine::CompileDecodeShader(int errorMessageSize, char* errorMessage)
+{
+ return mShaderPrograms.CompileDecodeShader(errorMessageSize, errorMessage);
+}
+
+bool RenderEngine::CompileOutputPackShader(int errorMessageSize, char* errorMessage)
+{
+ return mShaderPrograms.CompileOutputPackShader(errorMessageSize, errorMessage);
+}
+
+bool RenderEngine::InitializeResources(
+ unsigned inputFrameWidth,
+ unsigned inputFrameHeight,
+ unsigned captureTextureWidth,
+ unsigned outputFrameWidth,
+ unsigned outputFrameHeight,
+ unsigned outputPackTextureWidth,
+ std::string& error)
+{
+ return mRenderer.InitializeResources(
+ inputFrameWidth,
+ inputFrameHeight,
+ captureTextureWidth,
+ outputFrameWidth,
+ outputFrameHeight,
+ outputPackTextureWidth,
+ error);
+}
+
+bool RenderEngine::CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage)
+{
+ return mShaderPrograms.CompileLayerPrograms(inputFrameWidth, inputFrameHeight, errorMessageSize, errorMessage);
+}
+
+bool RenderEngine::CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage)
+{
+ return mShaderPrograms.CommitPreparedLayerPrograms(preparedBuild, inputFrameWidth, inputFrameHeight, errorMessageSize, errorMessage);
+}
+
+const std::vector& RenderEngine::CommittedLayerStates() const
+{
+ return mShaderPrograms.CommittedLayerStates();
+}
+
+void RenderEngine::ResetTemporalHistoryState()
+{
+ mShaderPrograms.ResetTemporalHistoryState();
+}
+
+void RenderEngine::ResetShaderFeedbackState()
+{
+ mShaderPrograms.ResetShaderFeedbackState();
+}
+
+void RenderEngine::ResizeView(int width, int height)
+{
+ mRenderer.ResizeView(width, height);
+}
+
+bool RenderEngine::TryPresentToWindow(unsigned outputFrameWidth, unsigned outputFrameHeight)
+{
+ if (!TryEnterCriticalSection(&mMutex))
+ return false;
+
+ mRenderer.PresentToWindow(mHdc, outputFrameWidth, outputFrameHeight);
+ LeaveCriticalSection(&mMutex);
+ return true;
+}
+
+bool RenderEngine::TryUploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& videoState)
+{
+ if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr)
+ return true;
+
+ const long textureSize = inputFrame.rowBytes * static_cast(inputFrame.height);
+ if (!TryEnterCriticalSection(&mMutex))
+ return false;
+
+ wglMakeCurrent(mHdc, mHglrc);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mRenderer.TextureUploadBuffer());
+ glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, inputFrame.bytes, GL_DYNAMIC_DRAW);
+ glBindTexture(GL_TEXTURE_2D, mRenderer.CaptureTexture());
+ if (inputFrame.pixelFormat == VideoIOPixelFormat::V210)
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, videoState.captureTextureWidth, videoState.inputFrameSize.height, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ else
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, videoState.captureTextureWidth, videoState.inputFrameSize.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+ wglMakeCurrent(NULL, NULL);
+ LeaveCriticalSection(&mMutex);
+ return true;
+}
+
+bool RenderEngine::RenderOutputFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame)
+{
+ EnterCriticalSection(&mMutex);
+ wglMakeCurrent(mHdc, mHglrc);
+ const bool rendered = mRenderPipeline.RenderFrame(context, outputFrame);
+ wglMakeCurrent(NULL, NULL);
+ LeaveCriticalSection(&mMutex);
+ return rendered;
+}
+
+void RenderEngine::RenderLayerStack(
+ bool hasInputSource,
+ const std::vector& layerStates,
+ unsigned inputFrameWidth,
+ unsigned inputFrameHeight,
+ unsigned captureTextureWidth,
+ VideoIOPixelFormat inputPixelFormat,
+ unsigned historyCap)
+{
+ mRenderPass.Render(
+ hasInputSource,
+ layerStates,
+ inputFrameWidth,
+ inputFrameHeight,
+ captureTextureWidth,
+ inputPixelFormat,
+ historyCap,
+ [this](const RuntimeRenderState& state, OpenGLRenderer::LayerProgram::TextBinding& textBinding, std::string& error) {
+ return mShaderPrograms.UpdateTextBindingTexture(state, textBinding, error);
+ },
+ [this](const RuntimeRenderState& state, unsigned availableSourceHistoryLength, unsigned availableTemporalHistoryLength, bool feedbackAvailable) {
+ return mShaderPrograms.UpdateGlobalParamsBuffer(state, availableSourceHistoryLength, availableTemporalHistoryLength, feedbackAvailable);
+ });
+}
+
+bool RenderEngine::ReadOutputFrameRgba(unsigned width, unsigned height, std::vector& bottomUpPixels)
+{
+ if (width == 0 || height == 0)
+ return false;
+
+ EnterCriticalSection(&mMutex);
+ wglMakeCurrent(mHdc, mHglrc);
+
+ bottomUpPixels.resize(static_cast(width) * height * 4);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputFramebuffer());
+ glReadBuffer(GL_COLOR_ATTACHMENT0);
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bottomUpPixels.data());
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+ wglMakeCurrent(NULL, NULL);
+ LeaveCriticalSection(&mMutex);
+ return true;
+}
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.h b/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.h
new file mode 100644
index 0000000..f9007f3
--- /dev/null
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/RenderEngine.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include "OpenGLRenderPass.h"
+#include "OpenGLRenderPipeline.h"
+#include "OpenGLRenderer.h"
+#include "OpenGLShaderPrograms.h"
+#include "RuntimeHost.h"
+#include "RuntimeSnapshotProvider.h"
+
+#include
+
+#include
+#include
+#include
+
+class RenderEngine
+{
+public:
+ using RenderEffectCallback = std::function;
+ using ScreenshotCallback = std::function;
+ using PreviewPaintCallback = std::function;
+
+ RenderEngine(
+ RuntimeHost& runtimeHost,
+ RuntimeSnapshotProvider& runtimeSnapshotProvider,
+ CRITICAL_SECTION& mutex,
+ HDC hdc,
+ HGLRC hglrc,
+ RenderEffectCallback renderEffect,
+ ScreenshotCallback screenshotReady,
+ PreviewPaintCallback previewPaint);
+ ~RenderEngine();
+
+ bool CompileDecodeShader(int errorMessageSize, char* errorMessage);
+ bool CompileOutputPackShader(int errorMessageSize, char* errorMessage);
+ bool InitializeResources(
+ unsigned inputFrameWidth,
+ unsigned inputFrameHeight,
+ unsigned captureTextureWidth,
+ unsigned outputFrameWidth,
+ unsigned outputFrameHeight,
+ unsigned outputPackTextureWidth,
+ std::string& error);
+ bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
+ bool CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
+
+ const std::vector& CommittedLayerStates() const;
+ void ResetTemporalHistoryState();
+ void ResetShaderFeedbackState();
+ void ResizeView(int width, int height);
+ bool TryPresentToWindow(unsigned outputFrameWidth, unsigned outputFrameHeight);
+ bool TryUploadInputFrame(const VideoIOFrame& inputFrame, const VideoIOState& videoState);
+ bool RenderOutputFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame);
+ void RenderLayerStack(
+ bool hasInputSource,
+ const std::vector& layerStates,
+ unsigned inputFrameWidth,
+ unsigned inputFrameHeight,
+ unsigned captureTextureWidth,
+ VideoIOPixelFormat inputPixelFormat,
+ unsigned historyCap);
+ bool ReadOutputFrameRgba(unsigned width, unsigned height, std::vector& bottomUpPixels);
+
+private:
+ OpenGLRenderer mRenderer;
+ OpenGLRenderPass mRenderPass;
+ OpenGLRenderPipeline mRenderPipeline;
+ OpenGLShaderPrograms mShaderPrograms;
+ CRITICAL_SECTION& mMutex;
+ HDC mHdc;
+ HGLRC mHglrc;
+};
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp
index 11501c8..c819d08 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp
@@ -1,26 +1,17 @@
#include "OpenGLVideoIOBridge.h"
-#include "OpenGLRenderer.h"
-#include "RuntimeHost.h"
+#include "HealthTelemetry.h"
+#include "RenderEngine.h"
#include
-#include
OpenGLVideoIOBridge::OpenGLVideoIOBridge(
VideoIODevice& videoIO,
- OpenGLRenderer& renderer,
- OpenGLRenderPipeline& renderPipeline,
- RuntimeHost& runtimeHost,
- CRITICAL_SECTION& mutex,
- HDC hdc,
- HGLRC hglrc) :
+ RenderEngine& renderEngine,
+ HealthTelemetry& healthTelemetry) :
mVideoIO(videoIO),
- mRenderer(renderer),
- mRenderPipeline(renderPipeline),
- mRuntimeHost(runtimeHost),
- mMutex(mutex),
- mHdc(hdc),
- mHglrc(hglrc)
+ mRenderEngine(renderEngine),
+ mHealthTelemetry(healthTelemetry)
{
}
@@ -46,7 +37,7 @@ void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionRe
else if (completionResult == VideoIOCompletionResult::Flushed)
++mFlushedFrameCount;
- mRuntimeHost.GetHealthTelemetry().TryRecordFramePacingStats(
+ mHealthTelemetry.TryRecordFramePacingStats(
mCompletionIntervalMilliseconds,
mSmoothedCompletionIntervalMilliseconds,
mMaxCompletionIntervalMilliseconds,
@@ -58,38 +49,12 @@ void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionRe
void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame)
{
const VideoIOState& state = mVideoIO.State();
- mRuntimeHost.GetHealthTelemetry().TryReportSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
+ mHealthTelemetry.TryReportSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr)
return; // don't transfer texture when there's no input
- const long textureSize = inputFrame.rowBytes * static_cast(inputFrame.height);
-
- // Never let input upload stall the playout/render callback. If the GL bridge
- // is busy producing an output frame, skip this upload and use the next input.
- if (!TryEnterCriticalSection(&mMutex))
- return;
-
- wglMakeCurrent(mHdc, mHglrc); // make OpenGL context current in this thread
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mRenderer.TextureUploadBuffer());
- glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, inputFrame.bytes, GL_DYNAMIC_DRAW);
- glBindTexture(GL_TEXTURE_2D, mRenderer.CaptureTexture());
-
- // NULL for last arg indicates use current GL_PIXEL_UNPACK_BUFFER target as texture data.
- if (inputFrame.pixelFormat == VideoIOPixelFormat::V210)
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, state.captureTextureWidth, state.inputFrameSize.height, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- else
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, state.captureTextureWidth, state.inputFrameSize.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
-
- wglMakeCurrent(NULL, NULL);
-
- LeaveCriticalSection(&mMutex);
+ mRenderEngine.TryUploadInputFrame(inputFrame, state);
}
void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& completion)
@@ -104,15 +69,7 @@ void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& complet
frameContext.videoState = state;
frameContext.completion = completion;
- EnterCriticalSection(&mMutex);
-
- // make GL context current in this thread
- wglMakeCurrent(mHdc, mHglrc);
-
- mRenderPipeline.RenderFrame(frameContext, outputFrame);
- wglMakeCurrent(NULL, NULL);
-
- LeaveCriticalSection(&mMutex);
+ mRenderEngine.RenderOutputFrame(frameContext, outputFrame);
mVideoIO.EndOutputFrame(outputFrame);
diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h
index 7cca4f8..9f1db55 100644
--- a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h
+++ b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h
@@ -2,24 +2,19 @@
#include "OpenGLRenderPipeline.h"
-#include
-
#include
#include
-class RuntimeHost;
+class HealthTelemetry;
+class RenderEngine;
class OpenGLVideoIOBridge
{
public:
OpenGLVideoIOBridge(
VideoIODevice& videoIO,
- OpenGLRenderer& renderer,
- OpenGLRenderPipeline& renderPipeline,
- RuntimeHost& runtimeHost,
- CRITICAL_SECTION& mutex,
- HDC hdc,
- HGLRC hglrc);
+ RenderEngine& renderEngine,
+ HealthTelemetry& healthTelemetry);
void VideoFrameArrived(const VideoIOFrame& inputFrame);
void PlayoutFrameCompleted(const VideoIOCompletion& completion);
@@ -28,12 +23,8 @@ private:
void RecordFramePacing(VideoIOCompletionResult completionResult);
VideoIODevice& mVideoIO;
- OpenGLRenderer& mRenderer;
- OpenGLRenderPipeline& mRenderPipeline;
- RuntimeHost& mRuntimeHost;
- CRITICAL_SECTION& mMutex;
- HDC mHdc;
- HGLRC mHglrc;
+ RenderEngine& mRenderEngine;
+ HealthTelemetry& mHealthTelemetry;
std::chrono::steady_clock::time_point mLastPlayoutCompletionTime;
double mCompletionIntervalMilliseconds = 0.0;
double mSmoothedCompletionIntervalMilliseconds = 0.0;
diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.cpp b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.cpp
new file mode 100644
index 0000000..8b0a3ec
--- /dev/null
+++ b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.cpp
@@ -0,0 +1,175 @@
+#include "VideoBackend.h"
+
+#include "DeckLinkSession.h"
+#include "OpenGLVideoIOBridge.h"
+#include "RenderEngine.h"
+#include "HealthTelemetry.h"
+
+VideoBackend::VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry) :
+ mVideoIODevice(std::make_unique()),
+ mBridge(std::make_unique(*mVideoIODevice, renderEngine, healthTelemetry))
+{
+}
+
+VideoBackend::~VideoBackend()
+{
+ ReleaseResources();
+}
+
+void VideoBackend::ReleaseResources()
+{
+ if (mVideoIODevice)
+ mVideoIODevice->ReleaseResources();
+}
+
+bool VideoBackend::DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error)
+{
+ return mVideoIODevice->DiscoverDevicesAndModes(videoModes, error);
+}
+
+bool VideoBackend::SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error)
+{
+ return mVideoIODevice->SelectPreferredFormats(videoModes, outputAlphaRequired, error);
+}
+
+bool VideoBackend::ConfigureInput(const VideoFormat& inputVideoMode, std::string& error)
+{
+ return mVideoIODevice->ConfigureInput(
+ [this](const VideoIOFrame& frame) { mBridge->VideoFrameArrived(frame); },
+ inputVideoMode,
+ error);
+}
+
+bool VideoBackend::ConfigureOutput(const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error)
+{
+ return mVideoIODevice->ConfigureOutput(
+ [this](const VideoIOCompletion& completion) { mBridge->PlayoutFrameCompleted(completion); },
+ outputVideoMode,
+ externalKeyingEnabled,
+ error);
+}
+
+bool VideoBackend::Start()
+{
+ return mVideoIODevice->Start();
+}
+
+bool VideoBackend::Stop()
+{
+ return mVideoIODevice->Stop();
+}
+
+const VideoIOState& VideoBackend::State() const
+{
+ return mVideoIODevice->State();
+}
+
+VideoIOState& VideoBackend::MutableState()
+{
+ return mVideoIODevice->MutableState();
+}
+
+bool VideoBackend::BeginOutputFrame(VideoIOOutputFrame& frame)
+{
+ return mVideoIODevice->BeginOutputFrame(frame);
+}
+
+void VideoBackend::EndOutputFrame(VideoIOOutputFrame& frame)
+{
+ mVideoIODevice->EndOutputFrame(frame);
+}
+
+bool VideoBackend::ScheduleOutputFrame(const VideoIOOutputFrame& frame)
+{
+ return mVideoIODevice->ScheduleOutputFrame(frame);
+}
+
+void VideoBackend::AccountForCompletionResult(VideoIOCompletionResult result)
+{
+ mVideoIODevice->AccountForCompletionResult(result);
+}
+
+bool VideoBackend::HasInputDevice() const
+{
+ return mVideoIODevice->HasInputDevice();
+}
+
+bool VideoBackend::HasInputSource() const
+{
+ return mVideoIODevice->HasInputSource();
+}
+
+unsigned VideoBackend::InputFrameWidth() const
+{
+ return mVideoIODevice->InputFrameWidth();
+}
+
+unsigned VideoBackend::InputFrameHeight() const
+{
+ return mVideoIODevice->InputFrameHeight();
+}
+
+unsigned VideoBackend::OutputFrameWidth() const
+{
+ return mVideoIODevice->OutputFrameWidth();
+}
+
+unsigned VideoBackend::OutputFrameHeight() const
+{
+ return mVideoIODevice->OutputFrameHeight();
+}
+
+unsigned VideoBackend::CaptureTextureWidth() const
+{
+ return mVideoIODevice->CaptureTextureWidth();
+}
+
+unsigned VideoBackend::OutputPackTextureWidth() const
+{
+ return mVideoIODevice->OutputPackTextureWidth();
+}
+
+VideoIOPixelFormat VideoBackend::InputPixelFormat() const
+{
+ return mVideoIODevice->InputPixelFormat();
+}
+
+const std::string& VideoBackend::InputDisplayModeName() const
+{
+ return mVideoIODevice->InputDisplayModeName();
+}
+
+const std::string& VideoBackend::OutputModelName() const
+{
+ return mVideoIODevice->OutputModelName();
+}
+
+bool VideoBackend::SupportsInternalKeying() const
+{
+ return mVideoIODevice->SupportsInternalKeying();
+}
+
+bool VideoBackend::SupportsExternalKeying() const
+{
+ return mVideoIODevice->SupportsExternalKeying();
+}
+
+bool VideoBackend::KeyerInterfaceAvailable() const
+{
+ return mVideoIODevice->KeyerInterfaceAvailable();
+}
+
+bool VideoBackend::ExternalKeyingActive() const
+{
+ return mVideoIODevice->ExternalKeyingActive();
+}
+
+const std::string& VideoBackend::StatusMessage() const
+{
+ return mVideoIODevice->StatusMessage();
+}
+
+void VideoBackend::SetStatusMessage(const std::string& message)
+{
+ mVideoIODevice->SetStatusMessage(message);
+}
diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.h b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.h
new file mode 100644
index 0000000..d5a1702
--- /dev/null
+++ b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoBackend.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "VideoIOTypes.h"
+
+#include
+#include
+
+class HealthTelemetry;
+class OpenGLVideoIOBridge;
+class RenderEngine;
+class VideoIODevice;
+
+class VideoBackend
+{
+public:
+ VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry);
+ ~VideoBackend();
+
+ void ReleaseResources();
+ bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error);
+ bool SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error);
+ bool ConfigureInput(const VideoFormat& inputVideoMode, std::string& error);
+ bool ConfigureOutput(const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error);
+ bool Start();
+ bool Stop();
+
+ const VideoIOState& State() const;
+ VideoIOState& MutableState();
+ bool BeginOutputFrame(VideoIOOutputFrame& frame);
+ void EndOutputFrame(VideoIOOutputFrame& frame);
+ bool ScheduleOutputFrame(const VideoIOOutputFrame& frame);
+ void AccountForCompletionResult(VideoIOCompletionResult result);
+
+ bool HasInputDevice() const;
+ bool HasInputSource() const;
+ unsigned InputFrameWidth() const;
+ unsigned InputFrameHeight() const;
+ unsigned OutputFrameWidth() const;
+ unsigned OutputFrameHeight() const;
+ unsigned CaptureTextureWidth() const;
+ unsigned OutputPackTextureWidth() const;
+ VideoIOPixelFormat InputPixelFormat() const;
+ const std::string& InputDisplayModeName() const;
+ const std::string& OutputModelName() const;
+ bool SupportsInternalKeying() const;
+ bool SupportsExternalKeying() const;
+ bool KeyerInterfaceAvailable() const;
+ bool ExternalKeyingActive() const;
+ const std::string& StatusMessage() const;
+ void SetStatusMessage(const std::string& message);
+
+private:
+ std::unique_ptr mVideoIODevice;
+ std::unique_ptr mBridge;
+};