Revert "Video backend"

This reverts commit 4ffbb97abf.
This commit is contained in:
Aiden
2026-05-09 16:47:43 +10:00
parent 0c16665610
commit bc9aa6fbad
21 changed files with 275 additions and 512 deletions

View File

@@ -49,10 +49,6 @@ set(APP_SOURCES
"${APP_DIR}/videoio/decklink/DeckLinkSession.h" "${APP_DIR}/videoio/decklink/DeckLinkSession.h"
"${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.cpp" "${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.cpp"
"${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.h" "${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.h"
"${APP_DIR}/videoio/VideoIOBackendFactory.cpp"
"${APP_DIR}/videoio/VideoIOBackendFactory.h"
"${APP_DIR}/videoio/VideoIOConfig.cpp"
"${APP_DIR}/videoio/VideoIOConfig.h"
"${APP_DIR}/gl/renderer/GLExtensions.cpp" "${APP_DIR}/gl/renderer/GLExtensions.cpp"
"${APP_DIR}/gl/renderer/GLExtensions.h" "${APP_DIR}/gl/renderer/GLExtensions.h"
"${APP_DIR}/gl/shader/GlobalParamsBuffer.cpp" "${APP_DIR}/gl/shader/GlobalParamsBuffer.cpp"
@@ -208,35 +204,6 @@ endif()
add_test(NAME RuntimeParameterUtilsTests COMMAND RuntimeParameterUtilsTests) add_test(NAME RuntimeParameterUtilsTests COMMAND RuntimeParameterUtilsTests)
add_executable(RuntimeHostVideoIOStateTests
"${APP_DIR}/runtime/RuntimeHost.cpp"
"${APP_DIR}/runtime/RuntimeClock.cpp"
"${APP_DIR}/runtime/RuntimeJson.cpp"
"${APP_DIR}/runtime/RuntimeParameterUtils.cpp"
"${APP_DIR}/shader/ShaderCompiler.cpp"
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
"${APP_DIR}/videoio/VideoIOConfig.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RuntimeHostVideoIOStateTests.cpp"
)
target_include_directories(RuntimeHostVideoIOStateTests PRIVATE
"${APP_DIR}"
"${APP_DIR}/platform"
"${APP_DIR}/runtime"
"${APP_DIR}/shader"
"${APP_DIR}/videoio"
)
target_link_libraries(RuntimeHostVideoIOStateTests PRIVATE
Advapi32
)
if(MSVC)
target_compile_options(RuntimeHostVideoIOStateTests PRIVATE /W3)
endif()
add_test(NAME RuntimeHostVideoIOStateTests COMMAND RuntimeHostVideoIOStateTests)
add_executable(Std140BufferTests add_executable(Std140BufferTests
"${CMAKE_CURRENT_SOURCE_DIR}/tests/Std140BufferTests.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/tests/Std140BufferTests.cpp"
) )
@@ -351,7 +318,6 @@ endif()
add_test(NAME VideoPlayoutSchedulerTests COMMAND VideoPlayoutSchedulerTests) add_test(NAME VideoPlayoutSchedulerTests COMMAND VideoPlayoutSchedulerTests)
add_executable(VideoIODeviceFakeTests add_executable(VideoIODeviceFakeTests
"${APP_DIR}/videoio/VideoIOConfig.cpp"
"${APP_DIR}/videoio/VideoIOFormat.cpp" "${APP_DIR}/videoio/VideoIOFormat.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/tests/VideoIODeviceFakeTests.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/tests/VideoIODeviceFakeTests.cpp"
) )
@@ -368,43 +334,6 @@ endif()
add_test(NAME VideoIODeviceFakeTests COMMAND VideoIODeviceFakeTests) add_test(NAME VideoIODeviceFakeTests COMMAND VideoIODeviceFakeTests)
add_executable(VideoIOBackendFactoryTests
"${APP_DIR}/videoio/decklink/DeckLinkAPI_i.c"
"${APP_DIR}/videoio/decklink/DeckLinkSession.cpp"
"${APP_DIR}/videoio/decklink/DeckLinkSession.h"
"${APP_DIR}/videoio/decklink/DeckLinkDisplayMode.cpp"
"${APP_DIR}/videoio/decklink/DeckLinkDisplayMode.h"
"${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.cpp"
"${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.h"
"${APP_DIR}/videoio/decklink/DeckLinkFrameTransfer.cpp"
"${APP_DIR}/videoio/decklink/DeckLinkFrameTransfer.h"
"${APP_DIR}/videoio/VideoIOBackendFactory.cpp"
"${APP_DIR}/videoio/VideoIOBackendFactory.h"
"${APP_DIR}/videoio/VideoIOConfig.cpp"
"${APP_DIR}/videoio/VideoIOConfig.h"
"${APP_DIR}/videoio/VideoIOFormat.cpp"
"${APP_DIR}/videoio/VideoPlayoutScheduler.cpp"
"${APP_DIR}/videoio/VideoPlayoutScheduler.h"
"${CMAKE_CURRENT_SOURCE_DIR}/tests/VideoIOBackendFactoryTests.cpp"
)
target_include_directories(VideoIOBackendFactoryTests PRIVATE
"${APP_DIR}"
"${APP_DIR}/gl/renderer"
"${APP_DIR}/videoio"
"${APP_DIR}/videoio/decklink"
)
target_link_libraries(VideoIOBackendFactoryTests PRIVATE
Ole32
)
if(MSVC)
target_compile_options(VideoIOBackendFactoryTests PRIVATE /W3)
endif()
add_test(NAME VideoIOBackendFactoryTests COMMAND VideoIOBackendFactoryTests)
install(TARGETS LoopThroughWithOpenGLCompositing install(TARGETS LoopThroughWithOpenGLCompositing
RUNTIME DESTINATION "." RUNTIME DESTINATION "."
) )

View File

@@ -412,10 +412,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
break; break;
} }
// Setup OpenGL and video I/O capture/playout object // Setup OpenGL and DeckLink capture and playout object
pOpenGLComposite = new OpenGLComposite(hWnd, hDC, hRC); pOpenGLComposite = new OpenGLComposite(hWnd, hDC, hRC);
if (pOpenGLComposite->InitializeVideoIO()) if (pOpenGLComposite->InitDeckLink())
{ {
wglMakeCurrent( NULL, NULL ); wglMakeCurrent( NULL, NULL );
if (pOpenGLComposite->Start()) if (pOpenGLComposite->Start())
@@ -423,11 +423,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
PostMessage(hWnd, kCreateStatusStripMessage, 0, 0); PostMessage(hWnd, kCreateStatusStripMessage, 0, 0);
break; // success break; // success
} }
MessageBoxA(NULL, "The OpenGL/video I/O runtime initialized, but playout failed to start. See the previous start message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR); 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 else
{ {
MessageBoxA(NULL, "The OpenGL/video I/O runtime failed to initialize. See the previous initialization message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR); 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 // Failed to initialize - cleanup
@@ -438,7 +438,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
} }
catch (...) catch (...)
{ {
ShowUnhandledExceptionMessage("Startup failed while creating the OpenGL/video I/O runtime."); ShowUnhandledExceptionMessage("Startup failed while creating the OpenGL/DeckLink runtime.");
PostMessage(hWnd, WM_CLOSE, 0, 0); PostMessage(hWnd, WM_CLOSE, 0, 0);
break; break;
} }
@@ -474,7 +474,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
} }
catch (...) catch (...)
{ {
ShowUnhandledExceptionMessage("Shutdown failed while tearing down the OpenGL/video I/O runtime."); ShowUnhandledExceptionMessage("Shutdown failed while tearing down the OpenGL/DeckLink runtime.");
} }
// Deselect the current rendering context and delete it // Deselect the current rendering context and delete it

View File

@@ -1,3 +1,5 @@
#include "DeckLinkDisplayMode.h"
#include "DeckLinkSession.h"
#include "OpenGLComposite.h" #include "OpenGLComposite.h"
#include "GLExtensions.h" #include "GLExtensions.h"
#include "GlRenderConstants.h" #include "GlRenderConstants.h"
@@ -8,7 +10,6 @@
#include "PngScreenshotWriter.h" #include "PngScreenshotWriter.h"
#include "RuntimeServices.h" #include "RuntimeServices.h"
#include "ShaderBuildQueue.h" #include "ShaderBuildQueue.h"
#include "VideoIOBackendFactory.h"
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
@@ -22,6 +23,7 @@
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) : OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC), hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC),
mVideoIO(std::make_unique<DeckLinkSession>()),
mRenderer(std::make_unique<OpenGLRenderer>()), mRenderer(std::make_unique<OpenGLRenderer>()),
mUseCommittedLayerStates(false), mUseCommittedLayerStates(false),
mScreenshotRequested(false) mScreenshotRequested(false)
@@ -35,7 +37,7 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
[this]() { ProcessScreenshotRequest(); }, [this]() { ProcessScreenshotRequest(); },
[this]() { paintGL(); }); [this]() { paintGL(); });
mVideoIOBridge = std::make_unique<OpenGLVideoIOBridge>( mVideoIOBridge = std::make_unique<OpenGLVideoIOBridge>(
nullptr, *mVideoIO,
*mRenderer, *mRenderer,
*mRenderPipeline, *mRenderPipeline,
*mRuntimeHost, *mRuntimeHost,
@@ -54,15 +56,20 @@ OpenGLComposite::~OpenGLComposite()
mRuntimeServices->Stop(); mRuntimeServices->Stop();
if (mShaderBuildQueue) if (mShaderBuildQueue)
mShaderBuildQueue->Stop(); mShaderBuildQueue->Stop();
if (mVideoIO)
mVideoIO->ReleaseResources(); mVideoIO->ReleaseResources();
mRenderer->DestroyResources(); mRenderer->DestroyResources();
DeleteCriticalSection(&pMutex); DeleteCriticalSection(&pMutex);
} }
bool OpenGLComposite::InitializeVideoIO() bool OpenGLComposite::InitDeckLink()
{ {
return InitVideoIO();
}
bool OpenGLComposite::InitVideoIO()
{
VideoFormatSelection videoModes;
std::string initFailureReason; std::string initFailureReason;
if (mRuntimeHost && mRuntimeHost->GetRepoRoot().empty()) if (mRuntimeHost && mRuntimeHost->GetRepoRoot().empty())
@@ -75,31 +82,31 @@ bool OpenGLComposite::InitializeVideoIO()
} }
} }
if (!mRuntimeHost) if (mRuntimeHost)
{ {
initFailureReason = "Runtime host is not available."; if (!ResolveConfiguredVideoFormats(
MessageBoxA(NULL, initFailureReason.c_str(), "Video I/O initialization failed", MB_OK | MB_ICONERROR); mRuntimeHost->GetInputVideoFormat(),
mRuntimeHost->GetInputFrameRate(),
mRuntimeHost->GetOutputVideoFormat(),
mRuntimeHost->GetOutputFrameRate(),
videoModes,
initFailureReason))
{
MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink mode configuration error", MB_OK);
return false; return false;
} }
const VideoIOConfiguration videoIOConfig = mRuntimeHost->GetVideoIOConfiguration();
mVideoIO = CreateVideoIODevice(videoIOConfig.backendId, initFailureReason);
if (!mVideoIO)
{
MessageBoxA(NULL, initFailureReason.c_str(), "Video I/O initialization failed", MB_OK | MB_ICONERROR);
return false;
} }
mVideoIOBridge->SetVideoIODevice(mVideoIO.get());
if (!mVideoIO->DiscoverDevicesAndModes(videoIOConfig, initFailureReason)) if (!mVideoIO->DiscoverDevicesAndModes(videoModes, initFailureReason))
{ {
const char* title = initFailureReason == "Please install the Blackmagic DeckLink drivers to use the features of this application." const char* title = initFailureReason == "Please install the Blackmagic DeckLink drivers to use the features of this application."
? "This application requires the selected video I/O drivers installed." ? "This application requires the DeckLink drivers installed."
: "Video I/O initialization failed"; : "DeckLink initialization failed";
MessageBoxA(NULL, initFailureReason.c_str(), title, MB_OK | MB_ICONERROR); MessageBoxA(NULL, initFailureReason.c_str(), title, MB_OK | MB_ICONERROR);
return false; return false;
} }
if (!mVideoIO->SelectPreferredFormats(videoIOConfig, initFailureReason)) const bool outputAlphaRequired = mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled();
if (!mVideoIO->SelectPreferredFormats(videoModes, outputAlphaRequired, initFailureReason))
goto error; goto error;
if (! CheckOpenGLExtensions()) if (! CheckOpenGLExtensions())
@@ -114,9 +121,9 @@ bool OpenGLComposite::InitializeVideoIO()
goto error; goto error;
} }
PublishVideoIOStatus(mVideoIO->DeviceName().empty() PublishVideoIOStatus(mVideoIO->OutputModelName().empty()
? "Video I/O output device selected." ? "DeckLink output device selected."
: ("Selected output device: " + mVideoIO->DeviceName())); : ("Selected output device: " + mVideoIO->OutputModelName()));
// 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 (mVideoIO->OutputFrameWidth() < 1920) if (mVideoIO->OutputFrameWidth() < 1920)
@@ -124,7 +131,7 @@ bool OpenGLComposite::InitializeVideoIO()
else else
resizeWindow(mVideoIO->OutputFrameWidth() / 2, mVideoIO->OutputFrameHeight() / 2); resizeWindow(mVideoIO->OutputFrameWidth() / 2, mVideoIO->OutputFrameHeight() / 2);
if (!mVideoIO->ConfigureInput([this](const VideoIOFrame& frame) { mVideoIOBridge->VideoFrameArrived(frame); }, initFailureReason)) if (!mVideoIO->ConfigureInput([this](const VideoIOFrame& frame) { mVideoIOBridge->VideoFrameArrived(frame); }, videoModes.input, initFailureReason))
{ {
goto error; goto error;
} }
@@ -133,7 +140,7 @@ bool OpenGLComposite::InitializeVideoIO()
mRuntimeHost->SetSignalStatus(false, mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), mVideoIO->InputDisplayModeName()); mRuntimeHost->SetSignalStatus(false, mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), mVideoIO->InputDisplayModeName());
} }
if (!mVideoIO->ConfigureOutput([this](const VideoIOCompletion& completion) { mVideoIOBridge->PlayoutFrameCompleted(completion); }, initFailureReason)) if (!mVideoIO->ConfigureOutput([this](const VideoIOCompletion& completion) { mVideoIOBridge->PlayoutFrameCompleted(completion); }, videoModes.output, mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled(), initFailureReason))
{ {
goto error; goto error;
} }
@@ -144,16 +151,13 @@ bool OpenGLComposite::InitializeVideoIO()
error: error:
if (!initFailureReason.empty()) if (!initFailureReason.empty())
MessageBoxA(NULL, initFailureReason.c_str(), "Video I/O initialization failed", MB_OK | MB_ICONERROR); MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR);
mVideoIO->ReleaseResources(); mVideoIO->ReleaseResources();
return false; return false;
} }
void OpenGLComposite::paintGL() void OpenGLComposite::paintGL()
{ {
if (!mVideoIO)
return;
if (!TryEnterCriticalSection(&pMutex)) if (!TryEnterCriticalSection(&pMutex))
{ {
ValidateRect(hGLWnd, NULL); ValidateRect(hGLWnd, NULL);
@@ -183,13 +187,21 @@ void OpenGLComposite::resizeWindow(int width, int height)
void OpenGLComposite::PublishVideoIOStatus(const std::string& statusMessage) void OpenGLComposite::PublishVideoIOStatus(const std::string& statusMessage)
{ {
if (!mRuntimeHost || !mVideoIO) if (!mRuntimeHost)
return; return;
if (!statusMessage.empty()) if (!statusMessage.empty())
mVideoIO->SetStatusMessage(statusMessage); mVideoIO->SetStatusMessage(statusMessage);
mRuntimeHost->SetVideoIOStatus(mVideoIO->State()); mRuntimeHost->SetVideoIOStatus(
"decklink",
mVideoIO->OutputModelName(),
mVideoIO->SupportsInternalKeying(),
mVideoIO->SupportsExternalKeying(),
mVideoIO->KeyerInterfaceAvailable(),
mRuntimeHost->ExternalKeyingEnabled(),
mVideoIO->ExternalKeyingActive(),
mVideoIO->StatusMessage());
} }
bool OpenGLComposite::InitOpenGLState() bool OpenGLComposite::InitOpenGLState()

View File

@@ -39,7 +39,8 @@ public:
OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC); OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC);
~OpenGLComposite(); ~OpenGLComposite();
bool InitializeVideoIO(); bool InitDeckLink();
bool InitVideoIO();
bool Start(); bool Start();
bool Stop(); bool Stop();
bool ReloadShader(); bool ReloadShader();

View File

@@ -7,7 +7,7 @@
#include <gl/gl.h> #include <gl/gl.h>
OpenGLVideoIOBridge::OpenGLVideoIOBridge( OpenGLVideoIOBridge::OpenGLVideoIOBridge(
VideoIODevice* videoIO, VideoIODevice& videoIO,
OpenGLRenderer& renderer, OpenGLRenderer& renderer,
OpenGLRenderPipeline& renderPipeline, OpenGLRenderPipeline& renderPipeline,
RuntimeHost& runtimeHost, RuntimeHost& runtimeHost,
@@ -24,11 +24,6 @@ OpenGLVideoIOBridge::OpenGLVideoIOBridge(
{ {
} }
void OpenGLVideoIOBridge::SetVideoIODevice(VideoIODevice* videoIO)
{
mVideoIO = videoIO;
}
void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionResult) void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionResult)
{ {
const auto now = std::chrono::steady_clock::now(); const auto now = std::chrono::steady_clock::now();
@@ -62,10 +57,7 @@ void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionRe
void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame) void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame)
{ {
if (mVideoIO == nullptr) const VideoIOState& state = mVideoIO.State();
return;
const VideoIOState& state = mVideoIO->State();
mRuntimeHost.TrySetSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName); mRuntimeHost.TrySetSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr) if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr)
@@ -99,20 +91,17 @@ void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame)
void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& completion) void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& completion)
{ {
if (mVideoIO == nullptr)
return;
RecordFramePacing(completion.result); RecordFramePacing(completion.result);
EnterCriticalSection(&mMutex); EnterCriticalSection(&mMutex);
VideoIOOutputFrame outputFrame; VideoIOOutputFrame outputFrame;
if (!mVideoIO->BeginOutputFrame(outputFrame)) if (!mVideoIO.BeginOutputFrame(outputFrame))
{ {
LeaveCriticalSection(&mMutex); LeaveCriticalSection(&mMutex);
return; return;
} }
const VideoIOState& state = mVideoIO->State(); const VideoIOState& state = mVideoIO.State();
RenderPipelineFrameContext frameContext; RenderPipelineFrameContext frameContext;
frameContext.videoState = state; frameContext.videoState = state;
frameContext.completion = completion; frameContext.completion = completion;
@@ -122,12 +111,12 @@ void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& complet
mRenderPipeline.RenderFrame(frameContext, outputFrame); mRenderPipeline.RenderFrame(frameContext, outputFrame);
mVideoIO->EndOutputFrame(outputFrame); mVideoIO.EndOutputFrame(outputFrame);
mVideoIO->AccountForCompletionResult(completion.result); mVideoIO.AccountForCompletionResult(completion.result);
// Schedule the next frame for playout // Schedule the next frame for playout
mVideoIO->ScheduleOutputFrame(outputFrame); mVideoIO.ScheduleOutputFrame(outputFrame);
wglMakeCurrent(NULL, NULL); wglMakeCurrent(NULL, NULL);

View File

@@ -13,7 +13,7 @@ class OpenGLVideoIOBridge
{ {
public: public:
OpenGLVideoIOBridge( OpenGLVideoIOBridge(
VideoIODevice* videoIO, VideoIODevice& videoIO,
OpenGLRenderer& renderer, OpenGLRenderer& renderer,
OpenGLRenderPipeline& renderPipeline, OpenGLRenderPipeline& renderPipeline,
RuntimeHost& runtimeHost, RuntimeHost& runtimeHost,
@@ -21,15 +21,13 @@ public:
HDC hdc, HDC hdc,
HGLRC hglrc); HGLRC hglrc);
void SetVideoIODevice(VideoIODevice* videoIO);
void VideoFrameArrived(const VideoIOFrame& inputFrame); void VideoFrameArrived(const VideoIOFrame& inputFrame);
void PlayoutFrameCompleted(const VideoIOCompletion& completion); void PlayoutFrameCompleted(const VideoIOCompletion& completion);
private: private:
void RecordFramePacing(VideoIOCompletionResult completionResult); void RecordFramePacing(VideoIOCompletionResult completionResult);
VideoIODevice* mVideoIO; VideoIODevice& mVideoIO;
OpenGLRenderer& mRenderer; OpenGLRenderer& mRenderer;
OpenGLRenderPipeline& mRenderPipeline; OpenGLRenderPipeline& mRenderPipeline;
RuntimeHost& mRuntimeHost; RuntimeHost& mRuntimeHost;

View File

@@ -1228,20 +1228,25 @@ void RuntimeHost::MarkRenderStateDirtyLocked()
mRenderStateVersion.fetch_add(1, std::memory_order_relaxed); mRenderStateVersion.fetch_add(1, std::memory_order_relaxed);
} }
void RuntimeHost::SetVideoIOStatus(const VideoIOState& state) void RuntimeHost::SetDeckLinkOutputStatus(const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage)
{
SetVideoIOStatus("decklink", modelName, supportsInternalKeying, supportsExternalKeying, keyerInterfaceAvailable,
externalKeyingRequested, externalKeyingActive, statusMessage);
}
void RuntimeHost::SetVideoIOStatus(const std::string& backendName, const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage)
{ {
std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex);
mVideoIOStatus.backendId = state.backendId; mDeckLinkOutputStatus.backendName = backendName;
mVideoIOStatus.deviceName = state.deviceName; mDeckLinkOutputStatus.modelName = modelName;
mVideoIOStatus.capabilities = state.capabilities; mDeckLinkOutputStatus.supportsInternalKeying = supportsInternalKeying;
mVideoIOStatus.hasInputDevice = state.hasInputDevice; mDeckLinkOutputStatus.supportsExternalKeying = supportsExternalKeying;
mVideoIOStatus.hasInputSource = state.hasInputSource; mDeckLinkOutputStatus.keyerInterfaceAvailable = keyerInterfaceAvailable;
mVideoIOStatus.inputDisplayModeName = state.inputDisplayModeName; mDeckLinkOutputStatus.externalKeyingRequested = externalKeyingRequested;
mVideoIOStatus.outputDisplayModeName = state.outputDisplayModeName; mDeckLinkOutputStatus.externalKeyingActive = externalKeyingActive;
mVideoIOStatus.externalKeyingRequested = state.externalKeyingRequested; mDeckLinkOutputStatus.statusMessage = statusMessage;
mVideoIOStatus.externalKeyingActive = state.externalKeyingActive;
mVideoIOStatus.statusMessage = state.statusMessage;
mVideoIOStatus.formatStatusMessage = state.formatStatusMessage;
} }
void RuntimeHost::SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds) void RuntimeHost::SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
@@ -1476,67 +1481,61 @@ bool RuntimeHost::LoadConfig(std::string& error)
const double configuredValue = maxTemporalHistoryFramesValue->asNumber(static_cast<double>(mConfig.maxTemporalHistoryFrames)); const double configuredValue = maxTemporalHistoryFramesValue->asNumber(static_cast<double>(mConfig.maxTemporalHistoryFrames));
mConfig.maxTemporalHistoryFrames = configuredValue <= 0.0 ? 0u : static_cast<unsigned>(configuredValue); mConfig.maxTemporalHistoryFrames = configuredValue <= 0.0 ? 0u : static_cast<unsigned>(configuredValue);
} }
if (const JsonValue* videoBackendValue = configJson.find("videoBackend"))
{
VideoIOBackendId backendId = mConfig.videoIO.backendId;
if (videoBackendValue->isString() && ParseVideoIOBackendId(videoBackendValue->asString(), backendId))
mConfig.videoIO.backendId = backendId;
}
if (const JsonValue* enableExternalKeyingValue = configJson.find("enableExternalKeying")) if (const JsonValue* enableExternalKeyingValue = configJson.find("enableExternalKeying"))
mConfig.videoIO.externalKeyingEnabled = enableExternalKeyingValue->asBoolean(mConfig.videoIO.externalKeyingEnabled); mConfig.enableExternalKeying = enableExternalKeyingValue->asBoolean(mConfig.enableExternalKeying);
if (const JsonValue* videoFormatValue = configJson.find("videoFormat")) if (const JsonValue* videoFormatValue = configJson.find("videoFormat"))
{ {
if (videoFormatValue->isString() && !videoFormatValue->asString().empty()) if (videoFormatValue->isString() && !videoFormatValue->asString().empty())
{ {
mConfig.videoIO.inputMode.videoFormat = videoFormatValue->asString(); mConfig.inputVideoFormat = videoFormatValue->asString();
mConfig.videoIO.outputMode.videoFormat = videoFormatValue->asString(); mConfig.outputVideoFormat = videoFormatValue->asString();
} }
} }
if (const JsonValue* frameRateValue = configJson.find("frameRate")) if (const JsonValue* frameRateValue = configJson.find("frameRate"))
{ {
if (frameRateValue->isString() && !frameRateValue->asString().empty()) if (frameRateValue->isString() && !frameRateValue->asString().empty())
{ {
mConfig.videoIO.inputMode.frameRate = frameRateValue->asString(); mConfig.inputFrameRate = frameRateValue->asString();
mConfig.videoIO.outputMode.frameRate = frameRateValue->asString(); mConfig.outputFrameRate = frameRateValue->asString();
} }
else if (frameRateValue->isNumber()) else if (frameRateValue->isNumber())
{ {
std::ostringstream stream; std::ostringstream stream;
stream << frameRateValue->asNumber(); stream << frameRateValue->asNumber();
mConfig.videoIO.inputMode.frameRate = stream.str(); mConfig.inputFrameRate = stream.str();
mConfig.videoIO.outputMode.frameRate = stream.str(); mConfig.outputFrameRate = stream.str();
} }
} }
if (const JsonValue* inputVideoFormatValue = configJson.find("inputVideoFormat")) if (const JsonValue* inputVideoFormatValue = configJson.find("inputVideoFormat"))
{ {
if (inputVideoFormatValue->isString() && !inputVideoFormatValue->asString().empty()) if (inputVideoFormatValue->isString() && !inputVideoFormatValue->asString().empty())
mConfig.videoIO.inputMode.videoFormat = inputVideoFormatValue->asString(); mConfig.inputVideoFormat = inputVideoFormatValue->asString();
} }
if (const JsonValue* inputFrameRateValue = configJson.find("inputFrameRate")) if (const JsonValue* inputFrameRateValue = configJson.find("inputFrameRate"))
{ {
if (inputFrameRateValue->isString() && !inputFrameRateValue->asString().empty()) if (inputFrameRateValue->isString() && !inputFrameRateValue->asString().empty())
mConfig.videoIO.inputMode.frameRate = inputFrameRateValue->asString(); mConfig.inputFrameRate = inputFrameRateValue->asString();
else if (inputFrameRateValue->isNumber()) else if (inputFrameRateValue->isNumber())
{ {
std::ostringstream stream; std::ostringstream stream;
stream << inputFrameRateValue->asNumber(); stream << inputFrameRateValue->asNumber();
mConfig.videoIO.inputMode.frameRate = stream.str(); mConfig.inputFrameRate = stream.str();
} }
} }
if (const JsonValue* outputVideoFormatValue = configJson.find("outputVideoFormat")) if (const JsonValue* outputVideoFormatValue = configJson.find("outputVideoFormat"))
{ {
if (outputVideoFormatValue->isString() && !outputVideoFormatValue->asString().empty()) if (outputVideoFormatValue->isString() && !outputVideoFormatValue->asString().empty())
mConfig.videoIO.outputMode.videoFormat = outputVideoFormatValue->asString(); mConfig.outputVideoFormat = outputVideoFormatValue->asString();
} }
if (const JsonValue* outputFrameRateValue = configJson.find("outputFrameRate")) if (const JsonValue* outputFrameRateValue = configJson.find("outputFrameRate"))
{ {
if (outputFrameRateValue->isString() && !outputFrameRateValue->asString().empty()) if (outputFrameRateValue->isString() && !outputFrameRateValue->asString().empty())
mConfig.videoIO.outputMode.frameRate = outputFrameRateValue->asString(); mConfig.outputFrameRate = outputFrameRateValue->asString();
else if (outputFrameRateValue->isNumber()) else if (outputFrameRateValue->isNumber())
{ {
std::ostringstream stream; std::ostringstream stream;
stream << outputFrameRateValue->asNumber(); stream << outputFrameRateValue->asNumber();
mConfig.videoIO.outputMode.frameRate = stream.str(); mConfig.outputFrameRate = stream.str();
} }
} }
@@ -1868,12 +1867,11 @@ JsonValue RuntimeHost::BuildStateValue() const
app.set("oscPort", JsonValue(static_cast<double>(mConfig.oscPort))); app.set("oscPort", JsonValue(static_cast<double>(mConfig.oscPort)));
app.set("autoReload", JsonValue(mAutoReloadEnabled)); app.set("autoReload", JsonValue(mAutoReloadEnabled));
app.set("maxTemporalHistoryFrames", JsonValue(static_cast<double>(mConfig.maxTemporalHistoryFrames))); app.set("maxTemporalHistoryFrames", JsonValue(static_cast<double>(mConfig.maxTemporalHistoryFrames)));
app.set("videoBackend", JsonValue(VideoIOBackendName(mConfig.videoIO.backendId))); app.set("enableExternalKeying", JsonValue(mConfig.enableExternalKeying));
app.set("enableExternalKeying", JsonValue(mConfig.videoIO.externalKeyingEnabled)); app.set("inputVideoFormat", JsonValue(mConfig.inputVideoFormat));
app.set("inputVideoFormat", JsonValue(mConfig.videoIO.inputMode.videoFormat)); app.set("inputFrameRate", JsonValue(mConfig.inputFrameRate));
app.set("inputFrameRate", JsonValue(mConfig.videoIO.inputMode.frameRate)); app.set("outputVideoFormat", JsonValue(mConfig.outputVideoFormat));
app.set("outputVideoFormat", JsonValue(mConfig.videoIO.outputMode.videoFormat)); app.set("outputFrameRate", JsonValue(mConfig.outputFrameRate));
app.set("outputFrameRate", JsonValue(mConfig.videoIO.outputMode.frameRate));
root.set("app", app); root.set("app", app);
JsonValue runtime = JsonValue::MakeObject(); JsonValue runtime = JsonValue::MakeObject();
@@ -1889,22 +1887,25 @@ JsonValue RuntimeHost::BuildStateValue() const
video.set("modeName", JsonValue(mSignalModeName)); video.set("modeName", JsonValue(mSignalModeName));
root.set("video", video); root.set("video", video);
JsonValue deckLink = JsonValue::MakeObject();
deckLink.set("modelName", JsonValue(mDeckLinkOutputStatus.modelName));
deckLink.set("supportsInternalKeying", JsonValue(mDeckLinkOutputStatus.supportsInternalKeying));
deckLink.set("supportsExternalKeying", JsonValue(mDeckLinkOutputStatus.supportsExternalKeying));
deckLink.set("keyerInterfaceAvailable", JsonValue(mDeckLinkOutputStatus.keyerInterfaceAvailable));
deckLink.set("externalKeyingRequested", JsonValue(mDeckLinkOutputStatus.externalKeyingRequested));
deckLink.set("externalKeyingActive", JsonValue(mDeckLinkOutputStatus.externalKeyingActive));
deckLink.set("statusMessage", JsonValue(mDeckLinkOutputStatus.statusMessage));
root.set("decklink", deckLink);
JsonValue videoIO = JsonValue::MakeObject(); JsonValue videoIO = JsonValue::MakeObject();
videoIO.set("backend", JsonValue(VideoIOBackendName(mVideoIOStatus.backendId))); videoIO.set("backend", JsonValue(mDeckLinkOutputStatus.backendName));
videoIO.set("deviceName", JsonValue(mVideoIOStatus.deviceName)); videoIO.set("modelName", JsonValue(mDeckLinkOutputStatus.modelName));
videoIO.set("hasInputDevice", JsonValue(mVideoIOStatus.hasInputDevice)); videoIO.set("supportsInternalKeying", JsonValue(mDeckLinkOutputStatus.supportsInternalKeying));
videoIO.set("hasInputSource", JsonValue(mVideoIOStatus.hasInputSource)); videoIO.set("supportsExternalKeying", JsonValue(mDeckLinkOutputStatus.supportsExternalKeying));
videoIO.set("inputModeName", JsonValue(mVideoIOStatus.inputDisplayModeName)); videoIO.set("keyerInterfaceAvailable", JsonValue(mDeckLinkOutputStatus.keyerInterfaceAvailable));
videoIO.set("outputModeName", JsonValue(mVideoIOStatus.outputDisplayModeName)); videoIO.set("externalKeyingRequested", JsonValue(mDeckLinkOutputStatus.externalKeyingRequested));
JsonValue capabilities = JsonValue::MakeObject(); videoIO.set("externalKeyingActive", JsonValue(mDeckLinkOutputStatus.externalKeyingActive));
capabilities.set("supportsInternalKeying", JsonValue(mVideoIOStatus.capabilities.supportsInternalKeying)); videoIO.set("statusMessage", JsonValue(mDeckLinkOutputStatus.statusMessage));
capabilities.set("supportsExternalKeying", JsonValue(mVideoIOStatus.capabilities.supportsExternalKeying));
capabilities.set("keyerInterfaceAvailable", JsonValue(mVideoIOStatus.capabilities.keyerInterfaceAvailable));
videoIO.set("capabilities", capabilities);
videoIO.set("externalKeyingRequested", JsonValue(mVideoIOStatus.externalKeyingRequested));
videoIO.set("externalKeyingActive", JsonValue(mVideoIOStatus.externalKeyingActive));
videoIO.set("statusMessage", JsonValue(mVideoIOStatus.statusMessage));
videoIO.set("formatStatusMessage", JsonValue(mVideoIOStatus.formatStatusMessage));
root.set("videoIO", videoIO); root.set("videoIO", videoIO);
JsonValue performance = JsonValue::MakeObject(); JsonValue performance = JsonValue::MakeObject();

View File

@@ -2,7 +2,6 @@
#include "RuntimeJson.h" #include "RuntimeJson.h"
#include "ShaderTypes.h" #include "ShaderTypes.h"
#include "VideoIOTypes.h"
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
@@ -39,7 +38,10 @@ public:
void SetCompileStatus(bool succeeded, const std::string& message); void SetCompileStatus(bool succeeded, const std::string& message);
void SetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName); void SetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
bool TrySetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName); bool TrySetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
void SetVideoIOStatus(const VideoIOState& state); void SetDeckLinkOutputStatus(const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage);
void SetVideoIOStatus(const std::string& backendName, const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage);
void SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds); void SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
bool TrySetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds); bool TrySetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
void SetFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds, void SetFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
@@ -63,8 +65,11 @@ public:
unsigned short GetServerPort() const { return mServerPort; } unsigned short GetServerPort() const { return mServerPort; }
unsigned short GetOscPort() const { return mConfig.oscPort; } unsigned short GetOscPort() const { return mConfig.oscPort; }
unsigned GetMaxTemporalHistoryFrames() const { return mConfig.maxTemporalHistoryFrames; } unsigned GetMaxTemporalHistoryFrames() const { return mConfig.maxTemporalHistoryFrames; }
bool ExternalKeyingEnabled() const { return mConfig.videoIO.externalKeyingEnabled; } bool ExternalKeyingEnabled() const { return mConfig.enableExternalKeying; }
VideoIOConfiguration GetVideoIOConfiguration() const { return mConfig.videoIO; } const std::string& GetInputVideoFormat() const { return mConfig.inputVideoFormat; }
const std::string& GetInputFrameRate() const { return mConfig.inputFrameRate; }
const std::string& GetOutputVideoFormat() const { return mConfig.outputVideoFormat; }
const std::string& GetOutputFrameRate() const { return mConfig.outputFrameRate; }
void SetServerPort(unsigned short port); void SetServerPort(unsigned short port);
bool AutoReloadEnabled() const { return mAutoReloadEnabled; } bool AutoReloadEnabled() const { return mAutoReloadEnabled; }
@@ -76,22 +81,23 @@ private:
unsigned short oscPort = 9000; unsigned short oscPort = 9000;
bool autoReload = true; bool autoReload = true;
unsigned maxTemporalHistoryFrames = 4; unsigned maxTemporalHistoryFrames = 4;
VideoIOConfiguration videoIO; bool enableExternalKeying = false;
std::string inputVideoFormat = "1080p";
std::string inputFrameRate = "59.94";
std::string outputVideoFormat = "1080p";
std::string outputFrameRate = "59.94";
}; };
struct VideoIOStatusSnapshot struct DeckLinkOutputStatus
{ {
VideoIOBackendId backendId = VideoIOBackendId::DeckLink; std::string backendName = "decklink";
std::string deviceName; std::string modelName;
VideoIOCapabilities capabilities; bool supportsInternalKeying = false;
bool hasInputDevice = false; bool supportsExternalKeying = false;
bool hasInputSource = false; bool keyerInterfaceAvailable = false;
std::string inputDisplayModeName = "1080p59.94";
std::string outputDisplayModeName = "1080p59.94";
bool externalKeyingRequested = false; bool externalKeyingRequested = false;
bool externalKeyingActive = false; bool externalKeyingActive = false;
std::string statusMessage; std::string statusMessage;
std::string formatStatusMessage;
}; };
struct LayerPersistentState struct LayerPersistentState
@@ -170,7 +176,7 @@ private:
uint64_t mLateFrameCount; uint64_t mLateFrameCount;
uint64_t mDroppedFrameCount; uint64_t mDroppedFrameCount;
uint64_t mFlushedFrameCount; uint64_t mFlushedFrameCount;
VideoIOStatusSnapshot mVideoIOStatus; DeckLinkOutputStatus mDeckLinkOutputStatus;
unsigned short mServerPort; unsigned short mServerPort;
bool mAutoReloadEnabled; bool mAutoReloadEnabled;
std::chrono::steady_clock::time_point mStartTime; std::chrono::steady_clock::time_point mStartTime;

View File

@@ -1,16 +0,0 @@
#include "VideoIOBackendFactory.h"
#include "DeckLinkSession.h"
#include "VideoIOTypes.h"
std::unique_ptr<VideoIODevice> CreateVideoIODevice(VideoIOBackendId backendId, std::string& error)
{
switch (backendId)
{
case VideoIOBackendId::DeckLink:
return std::make_unique<DeckLinkSession>();
}
error = "Unsupported video I/O backend.";
return nullptr;
}

View File

@@ -1,10 +0,0 @@
#pragma once
#include "VideoIOConfig.h"
#include <memory>
#include <string>
class VideoIODevice;
std::unique_ptr<VideoIODevice> CreateVideoIODevice(VideoIOBackendId backendId, std::string& error);

View File

@@ -1,35 +0,0 @@
#include "VideoIOConfig.h"
#include <algorithm>
#include <cctype>
namespace
{
std::string NormalizeToken(std::string value)
{
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
return value;
}
}
const char* VideoIOBackendName(VideoIOBackendId backendId)
{
switch (backendId)
{
case VideoIOBackendId::DeckLink:
return "decklink";
}
return "unknown";
}
bool ParseVideoIOBackendId(const std::string& value, VideoIOBackendId& backendId)
{
const std::string normalized = NormalizeToken(value);
if (normalized.empty() || normalized == "decklink")
{
backendId = VideoIOBackendId::DeckLink;
return true;
}
return false;
}

View File

@@ -1,44 +0,0 @@
#pragma once
#include <string>
enum class VideoIOBackendId
{
DeckLink
};
const char* VideoIOBackendName(VideoIOBackendId backendId);
bool ParseVideoIOBackendId(const std::string& value, VideoIOBackendId& backendId);
struct FrameSize
{
unsigned width = 0;
unsigned height = 0;
bool IsEmpty() const { return width == 0 || height == 0; }
};
inline bool operator==(const FrameSize& left, const FrameSize& right)
{
return left.width == right.width && left.height == right.height;
}
inline bool operator!=(const FrameSize& left, const FrameSize& right)
{
return !(left == right);
}
struct VideoIOModeConfiguration
{
std::string videoFormat = "1080p";
std::string frameRate = "59.94";
};
struct VideoIOConfiguration
{
VideoIOBackendId backendId = VideoIOBackendId::DeckLink;
VideoIOModeConfiguration inputMode;
VideoIOModeConfiguration outputMode;
bool externalKeyingEnabled = false;
bool preferTenBit = true;
};

View File

@@ -1,17 +1,15 @@
#pragma once #pragma once
#include "VideoIOConfig.h" #include "DeckLinkDisplayMode.h"
#include "VideoIOFormat.h" #include "VideoIOFormat.h"
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <string> #include <string>
struct VideoIOCapabilities enum class VideoIOBackend
{ {
bool supportsInternalKeying = false; DeckLink
bool supportsExternalKeying = false;
bool keyerInterfaceAvailable = false;
}; };
enum class VideoIOCompletionResult enum class VideoIOCompletionResult
@@ -23,9 +21,15 @@ enum class VideoIOCompletionResult
Unknown Unknown
}; };
struct VideoIOConfig
{
VideoFormatSelection videoModes;
bool externalKeyingEnabled = false;
bool preferTenBit = true;
};
struct VideoIOState struct VideoIOState
{ {
VideoIOBackendId backendId = VideoIOBackendId::DeckLink;
FrameSize inputFrameSize; FrameSize inputFrameSize;
FrameSize outputFrameSize; FrameSize outputFrameSize;
VideoIOPixelFormat inputPixelFormat = VideoIOPixelFormat::Uyvy8; VideoIOPixelFormat inputPixelFormat = VideoIOPixelFormat::Uyvy8;
@@ -36,13 +40,14 @@ struct VideoIOState
unsigned outputPackTextureWidth = 0; unsigned outputPackTextureWidth = 0;
std::string inputDisplayModeName = "1080p59.94"; std::string inputDisplayModeName = "1080p59.94";
std::string outputDisplayModeName = "1080p59.94"; std::string outputDisplayModeName = "1080p59.94";
std::string deviceName; std::string outputModelName;
std::string statusMessage; std::string statusMessage;
std::string formatStatusMessage; std::string formatStatusMessage;
bool hasInputDevice = false; bool hasInputDevice = false;
bool hasInputSource = false; bool hasInputSource = false;
VideoIOCapabilities capabilities; bool supportsInternalKeying = false;
bool externalKeyingRequested = false; bool supportsExternalKeying = false;
bool keyerInterfaceAvailable = false;
bool externalKeyingActive = false; bool externalKeyingActive = false;
double frameBudgetMilliseconds = 0.0; double frameBudgetMilliseconds = 0.0;
}; };
@@ -88,12 +93,11 @@ public:
using OutputFrameCallback = std::function<void(const VideoIOCompletion&)>; using OutputFrameCallback = std::function<void(const VideoIOCompletion&)>;
virtual ~VideoIODevice() = default; virtual ~VideoIODevice() = default;
virtual VideoIOBackendId BackendId() const = 0;
virtual void ReleaseResources() = 0; virtual void ReleaseResources() = 0;
virtual bool DiscoverDevicesAndModes(const VideoIOConfiguration& config, std::string& error) = 0; virtual bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error) = 0;
virtual bool SelectPreferredFormats(const VideoIOConfiguration& config, std::string& error) = 0; virtual bool SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error) = 0;
virtual bool ConfigureInput(InputFrameCallback callback, std::string& error) = 0; virtual bool ConfigureInput(InputFrameCallback callback, const VideoFormat& inputVideoMode, std::string& error) = 0;
virtual bool ConfigureOutput(OutputFrameCallback callback, std::string& error) = 0; virtual bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error) = 0;
virtual bool Start() = 0; virtual bool Start() = 0;
virtual bool Stop() = 0; virtual bool Stop() = 0;
virtual const VideoIOState& State() const = 0; virtual const VideoIOState& State() const = 0;
@@ -122,11 +126,10 @@ public:
unsigned OutputPackTextureWidth() const { return State().outputPackTextureWidth; } unsigned OutputPackTextureWidth() const { return State().outputPackTextureWidth; }
const std::string& FormatStatusMessage() const { return State().formatStatusMessage; } const std::string& FormatStatusMessage() const { return State().formatStatusMessage; }
const std::string& InputDisplayModeName() const { return State().inputDisplayModeName; } const std::string& InputDisplayModeName() const { return State().inputDisplayModeName; }
const std::string& DeviceName() const { return State().deviceName; } const std::string& OutputModelName() const { return State().outputModelName; }
bool SupportsInternalKeying() const { return State().capabilities.supportsInternalKeying; } bool SupportsInternalKeying() const { return State().supportsInternalKeying; }
bool SupportsExternalKeying() const { return State().capabilities.supportsExternalKeying; } bool SupportsExternalKeying() const { return State().supportsExternalKeying; }
bool KeyerInterfaceAvailable() const { return State().capabilities.keyerInterfaceAvailable; } bool KeyerInterfaceAvailable() const { return State().keyerInterfaceAvailable; }
bool ExternalKeyingRequested() const { return State().externalKeyingRequested; }
bool ExternalKeyingActive() const { return State().externalKeyingActive; } bool ExternalKeyingActive() const { return State().externalKeyingActive; }
const std::string& StatusMessage() const { return State().statusMessage; } const std::string& StatusMessage() const { return State().statusMessage; }
double FrameBudgetMilliseconds() const { return State().frameBudgetMilliseconds; } double FrameBudgetMilliseconds() const { return State().frameBudgetMilliseconds; }

View File

@@ -13,10 +13,10 @@ std::string NormalizeModeToken(const std::string& value)
return normalized; return normalized;
} }
bool ResolveConfiguredDeckLinkDisplayMode(const VideoIOModeConfiguration& mode, BMDDisplayMode& displayMode, std::string& displayModeName) bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName)
{ {
DeckLinkVideoMode videoMode; VideoFormat videoMode;
if (!ResolveConfiguredDeckLinkVideoMode(mode, videoMode)) if (!ResolveConfiguredVideoFormat(videoFormat, frameRate, videoMode))
return false; return false;
displayMode = videoMode.displayMode; displayMode = videoMode.displayMode;
@@ -24,10 +24,10 @@ bool ResolveConfiguredDeckLinkDisplayMode(const VideoIOModeConfiguration& mode,
return true; return true;
} }
bool ResolveConfiguredDeckLinkVideoMode(const VideoIOModeConfiguration& mode, DeckLinkVideoMode& videoMode) bool ResolveConfiguredVideoFormat(const std::string& videoFormat, const std::string& frameRate, VideoFormat& videoMode)
{ {
const std::string formatToken = NormalizeModeToken(mode.videoFormat); const std::string formatToken = NormalizeModeToken(videoFormat);
const std::string frameToken = NormalizeModeToken(mode.frameRate); const std::string frameToken = NormalizeModeToken(frameRate);
const std::string combinedToken = formatToken + frameToken; const std::string combinedToken = formatToken + frameToken;
struct ModeOption struct ModeOption
@@ -98,22 +98,25 @@ bool ResolveConfiguredDeckLinkVideoMode(const VideoIOModeConfiguration& mode, De
return false; return false;
} }
bool ResolveConfiguredDeckLinkVideoModes( bool ResolveConfiguredVideoFormats(
const VideoIOConfiguration& config, const std::string& inputVideoFormat,
DeckLinkVideoModeSelection& videoModes, const std::string& inputFrameRate,
const std::string& outputVideoFormat,
const std::string& outputFrameRate,
VideoFormatSelection& videoModes,
std::string& error) std::string& error)
{ {
if (!ResolveConfiguredDeckLinkVideoMode(config.inputMode, videoModes.input)) if (!ResolveConfiguredVideoFormat(inputVideoFormat, inputFrameRate, videoModes.input))
{ {
error = "Unsupported DeckLink input mode in config/runtime-host.json: " + error = "Unsupported DeckLink inputVideoFormat/inputFrameRate in config/runtime-host.json: " +
config.inputMode.videoFormat + " / " + config.inputMode.frameRate; inputVideoFormat + " / " + inputFrameRate;
return false; return false;
} }
if (!ResolveConfiguredDeckLinkVideoMode(config.outputMode, videoModes.output)) if (!ResolveConfiguredVideoFormat(outputVideoFormat, outputFrameRate, videoModes.output))
{ {
error = "Unsupported DeckLink output mode in config/runtime-host.json: " + error = "Unsupported DeckLink outputVideoFormat/outputFrameRate in config/runtime-host.json: " +
config.outputMode.videoFormat + " / " + config.outputMode.frameRate; outputVideoFormat + " / " + outputFrameRate;
return false; return false;
} }

View File

@@ -1,27 +1,47 @@
#pragma once #pragma once
#include "DeckLinkAPI_h.h" #include "DeckLinkAPI_h.h"
#include "VideoIOConfig.h"
#include <string> #include <string>
struct DeckLinkVideoMode struct FrameSize
{
unsigned width = 0;
unsigned height = 0;
bool IsEmpty() const { return width == 0 || height == 0; }
};
inline bool operator==(const FrameSize& left, const FrameSize& right)
{
return left.width == right.width && left.height == right.height;
}
inline bool operator!=(const FrameSize& left, const FrameSize& right)
{
return !(left == right);
}
struct VideoFormat
{ {
BMDDisplayMode displayMode = bmdModeHD1080p5994; BMDDisplayMode displayMode = bmdModeHD1080p5994;
std::string displayName = "1080p59.94"; std::string displayName = "1080p59.94";
}; };
struct DeckLinkVideoModeSelection struct VideoFormatSelection
{ {
DeckLinkVideoMode input; VideoFormat input;
DeckLinkVideoMode output; VideoFormat output;
}; };
std::string NormalizeModeToken(const std::string& value); std::string NormalizeModeToken(const std::string& value);
bool ResolveConfiguredDeckLinkDisplayMode(const VideoIOModeConfiguration& mode, BMDDisplayMode& displayMode, std::string& displayModeName); bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName);
bool ResolveConfiguredDeckLinkVideoMode(const VideoIOModeConfiguration& mode, DeckLinkVideoMode& videoMode); bool ResolveConfiguredVideoFormat(const std::string& videoFormat, const std::string& frameRate, VideoFormat& videoMode);
bool ResolveConfiguredDeckLinkVideoModes( bool ResolveConfiguredVideoFormats(
const VideoIOConfiguration& config, const std::string& inputVideoFormat,
DeckLinkVideoModeSelection& videoModes, const std::string& inputFrameRate,
const std::string& outputVideoFormat,
const std::string& outputFrameRate,
VideoFormatSelection& videoModes,
std::string& error); std::string& error);
bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode); bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode);

View File

@@ -92,19 +92,14 @@ void DeckLinkSession::ReleaseResources()
output.Release(); output.Release();
} }
bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config, std::string& error) bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error)
{ {
CComPtr<IDeckLinkIterator> deckLinkIterator; CComPtr<IDeckLinkIterator> deckLinkIterator;
CComPtr<IDeckLinkDisplayMode> inputMode; CComPtr<IDeckLinkDisplayMode> inputMode;
CComPtr<IDeckLinkDisplayMode> outputMode; CComPtr<IDeckLinkDisplayMode> outputMode;
mState.backendId = BackendId(); mState.inputDisplayModeName = videoModes.input.displayName;
mState.externalKeyingRequested = config.externalKeyingEnabled; mState.outputDisplayModeName = videoModes.output.displayName;
if (!ResolveConfiguredDeckLinkVideoModes(config, mConfiguredModes, error))
return false;
mState.inputDisplayModeName = mConfiguredModes.input.displayName;
mState.outputDisplayModeName = mConfiguredModes.output.displayName;
HRESULT result = CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, reinterpret_cast<void**>(&deckLinkIterator)); HRESULT result = CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, reinterpret_cast<void**>(&deckLinkIterator));
if (FAILED(result)) if (FAILED(result))
@@ -156,9 +151,9 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config
output.Release(); output.Release();
else else
{ {
mState.deviceName = modelName; mState.outputModelName = modelName;
mState.capabilities.supportsInternalKeying = deviceSupportsInternalKeying; mState.supportsInternalKeying = deviceSupportsInternalKeying;
mState.capabilities.supportsExternalKeying = deviceSupportsExternalKeying; mState.supportsExternalKeying = deviceSupportsExternalKeying;
} }
} }
@@ -183,9 +178,9 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config
return false; return false;
} }
if (input && !FindDeckLinkDisplayMode(inputDisplayModeIterator, mConfiguredModes.input.displayMode, &inputMode)) if (input && !FindDeckLinkDisplayMode(inputDisplayModeIterator, videoModes.input.displayMode, &inputMode))
{ {
error = "Cannot get specified input BMDDisplayMode for configured mode: " + mConfiguredModes.input.displayName; error = "Cannot get specified input BMDDisplayMode for configured mode: " + videoModes.input.displayName;
ReleaseResources(); ReleaseResources();
return false; return false;
} }
@@ -199,9 +194,9 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config
return false; return false;
} }
if (!FindDeckLinkDisplayMode(outputDisplayModeIterator, mConfiguredModes.output.displayMode, &outputMode)) if (!FindDeckLinkDisplayMode(outputDisplayModeIterator, videoModes.output.displayMode, &outputMode))
{ {
error = "Cannot get specified output BMDDisplayMode for configured mode: " + mConfiguredModes.output.displayName; error = "Cannot get specified output BMDDisplayMode for configured mode: " + videoModes.output.displayName;
ReleaseResources(); ReleaseResources();
return false; return false;
} }
@@ -228,7 +223,7 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config
return true; return true;
} }
bool DeckLinkSession::SelectPreferredFormats(const VideoIOConfiguration& config, std::string& error) bool DeckLinkSession::SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error)
{ {
if (!output) if (!output)
{ {
@@ -238,19 +233,19 @@ bool DeckLinkSession::SelectPreferredFormats(const VideoIOConfiguration& config,
mState.formatStatusMessage.clear(); mState.formatStatusMessage.clear();
const bool inputTenBitSupported = input != nullptr && InputSupportsFormat(input, mConfiguredModes.input.displayMode, bmdFormat10BitYUV); const bool inputTenBitSupported = input != nullptr && InputSupportsFormat(input, videoModes.input.displayMode, bmdFormat10BitYUV);
mState.inputPixelFormat = input != nullptr ? ChoosePreferredVideoIOFormat(inputTenBitSupported) : VideoIOPixelFormat::Uyvy8; mState.inputPixelFormat = input != nullptr ? ChoosePreferredVideoIOFormat(inputTenBitSupported) : VideoIOPixelFormat::Uyvy8;
if (input != nullptr && !inputTenBitSupported) if (input != nullptr && !inputTenBitSupported)
mState.formatStatusMessage += "DeckLink input does not report 10-bit YUV support for the configured mode; using 8-bit capture. "; mState.formatStatusMessage += "DeckLink input does not report 10-bit YUV support for the configured mode; using 8-bit capture. ";
const bool outputTenBitSupported = OutputSupportsFormat(output, mConfiguredModes.output.displayMode, bmdFormat10BitYUV); const bool outputTenBitSupported = OutputSupportsFormat(output, videoModes.output.displayMode, bmdFormat10BitYUV);
const bool outputTenBitYuvaSupported = OutputSupportsFormat(output, mConfiguredModes.output.displayMode, bmdFormat10BitYUVA); const bool outputTenBitYuvaSupported = OutputSupportsFormat(output, videoModes.output.displayMode, bmdFormat10BitYUVA);
mState.outputPixelFormat = config.externalKeyingEnabled mState.outputPixelFormat = outputAlphaRequired
? (outputTenBitYuvaSupported ? VideoIOPixelFormat::Yuva10 : VideoIOPixelFormat::Bgra8) ? (outputTenBitYuvaSupported ? VideoIOPixelFormat::Yuva10 : VideoIOPixelFormat::Bgra8)
: (outputTenBitSupported ? VideoIOPixelFormat::V210 : VideoIOPixelFormat::Bgra8); : (outputTenBitSupported ? VideoIOPixelFormat::V210 : VideoIOPixelFormat::Bgra8);
if (config.externalKeyingEnabled && outputTenBitYuvaSupported) if (outputAlphaRequired && outputTenBitYuvaSupported)
mState.formatStatusMessage += "External keying requires alpha; using 10-bit YUVA output. "; mState.formatStatusMessage += "External keying requires alpha; using 10-bit YUVA output. ";
else if (config.externalKeyingEnabled) else if (outputAlphaRequired)
mState.formatStatusMessage += "External keying requires alpha, but DeckLink output does not report 10-bit YUVA support for the configured mode; using 8-bit BGRA output. "; mState.formatStatusMessage += "External keying requires alpha, but DeckLink output does not report 10-bit YUVA support for the configured mode; using 8-bit BGRA output. ";
else if (!outputTenBitSupported) else if (!outputTenBitSupported)
mState.formatStatusMessage += "DeckLink output does not report 10-bit YUV support for the configured mode; using 8-bit BGRA output. "; mState.formatStatusMessage += "DeckLink output does not report 10-bit YUV support for the configured mode; using 8-bit BGRA output. ";
@@ -291,7 +286,7 @@ bool DeckLinkSession::SelectPreferredFormats(const VideoIOConfiguration& config,
return true; return true;
} }
bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, std::string& error) bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, const VideoFormat& inputVideoMode, std::string& error)
{ {
mInputFrameCallback = std::move(callback); mInputFrameCallback = std::move(callback);
@@ -303,7 +298,7 @@ bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, std::string& e
} }
const BMDPixelFormat deckLinkInputPixelFormat = DeckLinkPixelFormatForVideoIO(mState.inputPixelFormat); const BMDPixelFormat deckLinkInputPixelFormat = DeckLinkPixelFormatForVideoIO(mState.inputPixelFormat);
if (input->EnableVideoInput(mConfiguredModes.input.displayMode, deckLinkInputPixelFormat, bmdVideoInputFlagDefault) != S_OK) if (input->EnableVideoInput(inputVideoMode.displayMode, deckLinkInputPixelFormat, bmdVideoInputFlagDefault) != S_OK)
{ {
if (mState.inputPixelFormat == VideoIOPixelFormat::V210) if (mState.inputPixelFormat == VideoIOPixelFormat::V210)
{ {
@@ -311,7 +306,7 @@ bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, std::string& e
mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8; mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8;
mState.inputFrameRowBytes = mState.inputFrameSize.width * 2u; mState.inputFrameRowBytes = mState.inputFrameSize.width * 2u;
mState.captureTextureWidth = mState.inputFrameSize.width / 2u; mState.captureTextureWidth = mState.inputFrameSize.width / 2u;
if (input->EnableVideoInput(mConfiguredModes.input.displayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault) == S_OK) if (input->EnableVideoInput(inputVideoMode.displayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault) == S_OK)
{ {
std::ostringstream status; std::ostringstream status;
status << "DeckLink formats: capture " << VideoIOPixelFormatName(mState.inputPixelFormat) status << "DeckLink formats: capture " << VideoIOPixelFormatName(mState.inputPixelFormat)
@@ -346,26 +341,26 @@ input_enabled:
return true; return true;
} }
bool DeckLinkSession::ConfigureOutput(OutputFrameCallback callback, std::string& error) bool DeckLinkSession::ConfigureOutput(OutputFrameCallback callback, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error)
{ {
mOutputFrameCallback = std::move(callback); mOutputFrameCallback = std::move(callback);
if (output->EnableVideoOutput(mConfiguredModes.output.displayMode, bmdVideoOutputFlagDefault) != S_OK) if (output->EnableVideoOutput(outputVideoMode.displayMode, bmdVideoOutputFlagDefault) != S_OK)
{ {
error = "DeckLink output setup failed while enabling video output."; error = "DeckLink output setup failed while enabling video output.";
return false; return false;
} }
if (output->QueryInterface(IID_IDeckLinkKeyer, (void**)&keyer) == S_OK && keyer != NULL) if (output->QueryInterface(IID_IDeckLinkKeyer, (void**)&keyer) == S_OK && keyer != NULL)
mState.capabilities.keyerInterfaceAvailable = true; mState.keyerInterfaceAvailable = true;
if (mState.externalKeyingRequested) if (externalKeyingEnabled)
{ {
if (!mState.capabilities.supportsExternalKeying) if (!mState.supportsExternalKeying)
{ {
mState.statusMessage = "External keying was requested, but the selected DeckLink output does not report external keying support."; mState.statusMessage = "External keying was requested, but the selected DeckLink output does not report external keying support.";
} }
else if (!mState.capabilities.keyerInterfaceAvailable) else if (!mState.keyerInterfaceAvailable)
{ {
mState.statusMessage = "External keying was requested, but the selected DeckLink output does not expose the IDeckLinkKeyer interface."; mState.statusMessage = "External keying was requested, but the selected DeckLink output does not expose the IDeckLinkKeyer interface.";
} }
@@ -379,7 +374,7 @@ bool DeckLinkSession::ConfigureOutput(OutputFrameCallback callback, std::string&
mState.statusMessage = "External keying is active on the selected DeckLink output."; mState.statusMessage = "External keying is active on the selected DeckLink output.";
} }
} }
else if (mState.capabilities.supportsExternalKeying) else if (mState.supportsExternalKeying)
{ {
mState.statusMessage = "Selected DeckLink output supports external keying. Set enableExternalKeying to true in runtime-host.json to request it."; mState.statusMessage = "Selected DeckLink output supports external keying. Set enableExternalKeying to true in runtime-host.json to request it.";
} }

View File

@@ -20,14 +20,41 @@ public:
DeckLinkSession() = default; DeckLinkSession() = default;
~DeckLinkSession(); ~DeckLinkSession();
VideoIOBackendId BackendId() const override { return VideoIOBackendId::DeckLink; }
void ReleaseResources() override; void ReleaseResources() override;
bool DiscoverDevicesAndModes(const VideoIOConfiguration& config, std::string& error) override; bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error) override;
bool SelectPreferredFormats(const VideoIOConfiguration& config, std::string& error) override; bool SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error) override;
bool ConfigureInput(InputFrameCallback callback, std::string& error) override; bool ConfigureInput(InputFrameCallback callback, const VideoFormat& inputVideoMode, std::string& error) override;
bool ConfigureOutput(OutputFrameCallback callback, std::string& error) override; bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error) override;
bool Start() override; bool Start() override;
bool Stop() override; bool Stop() override;
bool HasInputDevice() const { return mState.hasInputDevice; }
bool HasInputSource() const { return mState.hasInputSource; }
void SetInputSourceMissing(bool missing) { mState.hasInputSource = !missing; }
bool InputOutputDimensionsDiffer() const { return mState.inputFrameSize != mState.outputFrameSize; }
const FrameSize& InputFrameSize() const { return mState.inputFrameSize; }
const FrameSize& OutputFrameSize() const { return mState.outputFrameSize; }
unsigned InputFrameWidth() const { return mState.inputFrameSize.width; }
unsigned InputFrameHeight() const { return mState.inputFrameSize.height; }
unsigned OutputFrameWidth() const { return mState.outputFrameSize.width; }
unsigned OutputFrameHeight() const { return mState.outputFrameSize.height; }
VideoIOPixelFormat InputPixelFormat() const { return mState.inputPixelFormat; }
VideoIOPixelFormat OutputPixelFormat() const { return mState.outputPixelFormat; }
bool InputIsTenBit() const { return VideoIOPixelFormatIsTenBit(mState.inputPixelFormat); }
bool OutputIsTenBit() const { return VideoIOPixelFormatIsTenBit(mState.outputPixelFormat); }
unsigned InputFrameRowBytes() const { return mState.inputFrameRowBytes; }
unsigned OutputFrameRowBytes() const { return mState.outputFrameRowBytes; }
unsigned CaptureTextureWidth() const { return mState.captureTextureWidth; }
unsigned OutputPackTextureWidth() const { return mState.outputPackTextureWidth; }
const std::string& FormatStatusMessage() const { return mState.formatStatusMessage; }
const std::string& InputDisplayModeName() const { return mState.inputDisplayModeName; }
const std::string& OutputModelName() const { return mState.outputModelName; }
bool SupportsInternalKeying() const { return mState.supportsInternalKeying; }
bool SupportsExternalKeying() const { return mState.supportsExternalKeying; }
bool KeyerInterfaceAvailable() const { return mState.keyerInterfaceAvailable; }
bool ExternalKeyingActive() const { return mState.externalKeyingActive; }
const std::string& StatusMessage() const { return mState.statusMessage; }
void SetStatusMessage(const std::string& message) { mState.statusMessage = message; }
const VideoIOState& State() const override { return mState; } const VideoIOState& State() const override { return mState; }
VideoIOState& MutableState() override { return mState; } VideoIOState& MutableState() override { return mState; }
double FrameBudgetMilliseconds() const; double FrameBudgetMilliseconds() const;
@@ -49,5 +76,4 @@ private:
VideoPlayoutScheduler mScheduler; VideoPlayoutScheduler mScheduler;
InputFrameCallback mInputFrameCallback; InputFrameCallback mInputFrameCallback;
OutputFrameCallback mOutputFrameCallback; OutputFrameCallback mOutputFrameCallback;
DeckLinkVideoModeSelection mConfiguredModes;
}; };

View File

@@ -2,7 +2,6 @@
"shaderLibrary": "shaders", "shaderLibrary": "shaders",
"serverPort": 8080, "serverPort": 8080,
"oscPort": 9000, "oscPort": 9000,
"videoBackend": "decklink",
"inputVideoFormat": "1080p", "inputVideoFormat": "1080p",
"inputFrameRate": "59.94", "inputFrameRate": "59.94",
"outputVideoFormat": "1080p", "outputVideoFormat": "1080p",

View File

@@ -1,67 +0,0 @@
#include "RuntimeHost.h"
#include <iostream>
namespace
{
int gFailures = 0;
void Expect(bool condition, const char* message)
{
if (condition)
return;
std::cerr << "FAIL: " << message << "\n";
++gFailures;
}
}
int main()
{
RuntimeHost runtimeHost;
std::string error;
Expect(runtimeHost.Initialize(error), "runtime host initializes");
Expect(error.empty(), "runtime host initialization does not report an error");
VideoIOState state;
state.backendId = VideoIOBackendId::DeckLink;
state.deviceName = "Test Device";
state.hasInputDevice = true;
state.hasInputSource = true;
state.inputDisplayModeName = "fake input";
state.outputDisplayModeName = "fake output";
state.capabilities.supportsInternalKeying = true;
state.capabilities.supportsExternalKeying = true;
state.capabilities.keyerInterfaceAvailable = true;
state.externalKeyingRequested = true;
state.externalKeyingActive = true;
state.statusMessage = "ready";
state.formatStatusMessage = "using fake formats";
runtimeHost.SetVideoIOStatus(state);
JsonValue root;
Expect(ParseJson(runtimeHost.BuildStateJson(), root, error), "runtime state json parses");
Expect(root.find("videoIO") != nullptr, "runtime state exposes videoIO");
Expect(root.find("decklink") == nullptr, "runtime state no longer exposes a decklink top-level block");
const JsonValue* app = root.find("app");
Expect(app != nullptr, "runtime state exposes app settings");
Expect(app != nullptr && app->find("videoBackend") != nullptr, "app settings expose videoBackend");
Expect(app != nullptr && app->find("videoBackend")->asString() == "decklink", "videoBackend serializes as decklink");
const JsonValue* videoIO = root.find("videoIO");
Expect(videoIO != nullptr && videoIO->find("backend") != nullptr, "videoIO exposes backend");
Expect(videoIO != nullptr && videoIO->find("backend")->asString() == "decklink", "videoIO backend serializes as decklink");
Expect(videoIO != nullptr && videoIO->find("deviceName") != nullptr, "videoIO exposes device name");
Expect(videoIO != nullptr && videoIO->find("deviceName")->asString() == "Test Device", "videoIO device name matches");
Expect(videoIO != nullptr && videoIO->find("capabilities") != nullptr, "videoIO exposes capabilities");
if (gFailures != 0)
{
std::cerr << gFailures << " RuntimeHost video I/O state test failure(s).\n";
return 1;
}
std::cout << "RuntimeHost video I/O state tests passed.\n";
return 0;
}

View File

@@ -1,41 +0,0 @@
#include "VideoIOBackendFactory.h"
#include "VideoIOTypes.h"
#include <iostream>
namespace
{
int gFailures = 0;
void Expect(bool condition, const char* message)
{
if (condition)
return;
std::cerr << "FAIL: " << message << "\n";
++gFailures;
}
}
int main()
{
std::string error;
std::unique_ptr<VideoIODevice> device = CreateVideoIODevice(VideoIOBackendId::DeckLink, error);
Expect(device != nullptr, "decklink backend factory returns a device");
Expect(!device || device->BackendId() == VideoIOBackendId::DeckLink, "decklink backend reports decklink id");
Expect(error.empty(), "supported backend does not produce an error");
error.clear();
device = CreateVideoIODevice(static_cast<VideoIOBackendId>(999), error);
Expect(device == nullptr, "unknown backend id is rejected");
Expect(!error.empty(), "unknown backend reports an error");
if (gFailures != 0)
{
std::cerr << gFailures << " VideoIO backend factory test failure(s).\n";
return 1;
}
std::cout << "VideoIO backend factory tests passed.\n";
return 0;
}

View File

@@ -19,26 +19,20 @@ void Expect(bool condition, const char* message)
class FakeVideoIODevice : public VideoIODevice class FakeVideoIODevice : public VideoIODevice
{ {
public: public:
VideoIOBackendId BackendId() const override { return VideoIOBackendId::DeckLink; }
void ReleaseResources() override {} void ReleaseResources() override {}
bool DiscoverDevicesAndModes(const VideoIOConfiguration&, std::string&) override bool DiscoverDevicesAndModes(const VideoFormatSelection&, std::string&) override
{ {
mState.backendId = BackendId();
mState.inputFrameSize = { 1920, 1080 }; mState.inputFrameSize = { 1920, 1080 };
mState.outputFrameSize = { 1920, 1080 }; mState.outputFrameSize = { 1920, 1080 };
mState.inputDisplayModeName = "fake 1080p"; mState.inputDisplayModeName = "fake 1080p";
mState.outputDisplayModeName = "fake 1080p"; mState.outputModelName = "Fake Video IO";
mState.deviceName = "Fake Video IO";
mState.capabilities.supportsInternalKeying = true;
mState.capabilities.supportsExternalKeying = true;
mState.hasInputDevice = true; mState.hasInputDevice = true;
return true; return true;
} }
bool SelectPreferredFormats(const VideoIOConfiguration& config, std::string&) override bool SelectPreferredFormats(const VideoFormatSelection&, bool, std::string&) override
{ {
mState.externalKeyingRequested = config.externalKeyingEnabled;
mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8; mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8;
mState.outputPixelFormat = VideoIOPixelFormat::Bgra8; mState.outputPixelFormat = VideoIOPixelFormat::Bgra8;
mState.inputFrameRowBytes = VideoIORowBytes(mState.inputPixelFormat, mState.inputFrameSize.width); mState.inputFrameRowBytes = VideoIORowBytes(mState.inputPixelFormat, mState.inputFrameSize.width);
@@ -48,13 +42,13 @@ public:
return true; return true;
} }
bool ConfigureInput(InputFrameCallback callback, std::string&) override bool ConfigureInput(InputFrameCallback callback, const VideoFormat&, std::string&) override
{ {
mInputCallback = callback; mInputCallback = callback;
return true; return true;
} }
bool ConfigureOutput(OutputFrameCallback callback, std::string&) override bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat&, bool, std::string&) override
{ {
mOutputCallback = callback; mOutputCallback = callback;
return true; return true;
@@ -120,19 +114,19 @@ private:
int main() int main()
{ {
FakeVideoIODevice device; FakeVideoIODevice device;
VideoIOConfiguration config; VideoFormatSelection selection;
std::string error; std::string error;
bool inputSeen = false; bool inputSeen = false;
bool outputSeen = false; bool outputSeen = false;
Expect(device.DiscoverDevicesAndModes(config, error), "fake discovery succeeds"); Expect(device.DiscoverDevicesAndModes(selection, error), "fake discovery succeeds");
Expect(device.SelectPreferredFormats(config, error), "fake format selection succeeds"); Expect(device.SelectPreferredFormats(selection, false, error), "fake format selection succeeds");
Expect(device.ConfigureInput([&](const VideoIOFrame& frame) { Expect(device.ConfigureInput([&](const VideoIOFrame& frame) {
inputSeen = frame.bytes != nullptr && frame.width == 1920 && frame.pixelFormat == VideoIOPixelFormat::Uyvy8; inputSeen = frame.bytes != nullptr && frame.width == 1920 && frame.pixelFormat == VideoIOPixelFormat::Uyvy8;
}, error), "fake input config succeeds"); }, selection.input, error), "fake input config succeeds");
Expect(device.ConfigureOutput([&](const VideoIOCompletion& completion) { Expect(device.ConfigureOutput([&](const VideoIOCompletion& completion) {
outputSeen = completion.result == VideoIOCompletionResult::Completed; outputSeen = completion.result == VideoIOCompletionResult::Completed;
}, error), "fake output config succeeds"); }, selection.output, false, error), "fake output config succeeds");
Expect(device.Start(), "fake device starts"); Expect(device.Start(), "fake device starts");
VideoIOOutputFrame outputFrame; VideoIOOutputFrame outputFrame;